9

Is there a way in objective C that I can define a static int that is thread safe?

for example if I have class called Session that has:

static unsigned int session_id = 1000;

- (int) generateSessionID{
        return session_id++;
}

I am constructing session objects from different threads, each session object should have a unique id.

rcollyer
  • 10,475
  • 4
  • 48
  • 75
Unis
  • 257
  • 1
  • 6
  • 15
  • You need to use some sort of locking semantics. The issue is you want the operation to be atomic. But keep in mind that "++" is not atomic at all, it is a few operations at once. – BobbyShaftoe Oct 28 '10 at 02:25
  • is possible then to surround my return session_id++ with synchronized(self){return session_id++}. However I am not sure if this will lock the class or the object it self. – Unis Oct 28 '10 at 02:32
  • as written, it would lock the instance, not the class (not what you want). Perhaps "`@synchronized([self class])`"? – David Gelhar Oct 28 '10 at 02:45
  • 3
    @David Gelhar: No, do not synchronize on `[self class]` because instances of subclasses would sychronize on different class objects. Synchronize on an explicit class e.g. `[MyClass class]` – JeremyP Oct 28 '10 at 08:43

4 Answers4

9

I think you're better off using atomic operations to modify session_id. A previous question talks about atomic increment/decrement operations for OS X, and this page talks about the OSAtomic header file. Atomic operations on integers, something that's readily hardware-supported, will probably be substantially faster than using locking constructs.

Community
  • 1
  • 1
Wyatt Anderson
  • 9,612
  • 1
  • 22
  • 25
  • 1
    Agreed. If all you're doing is keeping around a static for the purposes of getting a global incrementing value, use `OSAtomicIncrement32`, which will be safe and not have all the overhead of Cocoa or POSIX synchronization primitives. – Ben Zotto Nov 20 '10 at 16:00
  • 1
    http://developer.apple.com/library/mac/#DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW14 – mcfedr Mar 23 '12 at 13:37
7

If you're talking Cocoa, the mutex functionality there is provided by NSLock and NSRecursiveLock.

In order to properly protect non-atomic resource, you need these mutexes, lest multiple threads may try to change the data at the same time (leading to corruption) or use the data in a half-changed state (leading to invalid data).

Your code would look something like this:

static NSLock session_id_lock;
static unsigned int session_id = 1000;

- (int) generateSessionID{
    int new_id;
    [myLock lock];
    new_id = session_id++;
    [myLock unlock];
    return new_id;
}

If you're not using Cocoa (or what little Cocoa programming I remember from my brief interlude with an iMac is so dimly remembered that it's near useless), just use the concept, translating it to whatever language or framework you have:

  • lock the mutex before using or changing a protected resource.
  • use or change the resource.
  • unlock the mutex.
  • bonus advice 1: lock the mutex as late as possible and unlock it as soon as possible.
  • bonus advice 2: only lock what you need so you avoid unnecessary delays.

Explaining that last point some more: if you synchronise on self for two totally unrelated things (say a session ID and a user ID), they will block each other despite the fact that it's not necessary to do so. I would prefer two separate mutexes to keep the granularity low.

Of course, if you only have a mutex on the session ID alone (but see below for caveat), feel free to use synchronized(self) but I'd prefer to do it my way so I wouldn't get caught out adding another protected resource later.

In any case (this is the caveat mentioned), you will probably find that synchronising on self would not adequately protect a static variable, which would be shared across multiple objects. The mutex should belong to the data rather than whatever is using it.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I am coming from Java world, where you simply can do: static synchronized int generateID(){return sessionId++}. is this will be compatible if i do -(int) generateSessionID{synchronized(self){return session_id++;}}? – Unis Oct 28 '10 at 02:42
  • I would think that would work okay though a lock on self (the object) is probably more broad than I would do. Instead, I would prefer a specific mutex so as to avoid mutex delays that were unnecessary (see my added bonus advice). – paxdiablo Oct 28 '10 at 02:45
  • @user489579 Close, but you don't want to synchronize on `self` for an instance method that's accessing a static variable; maybe sync on `[self class]`, or on an object that's directly associated with the value you're modifying (e.g. wrap the int in an NSNumber and sync on the NSNumber). – David Gelhar Oct 28 '10 at 02:47
  • Ah, good point, I forgot it was static so could be used by _multiple_ objects - I'll adjust the answer. – paxdiablo Oct 28 '10 at 02:50
  • David, I see your point and i thought of wrapping or declaring the a NSNumber instead of int, but where/when exactly you will release the NSNumber as it will be (can be) accessed by constructing other objects to generate their ids. – Unis Oct 28 '10 at 02:54
  • I wouldn't think you'd need to worry about releasing it. You could create it & initialize it to 0 in a `+initialize method` (see http://stackoverflow.com/questions/992070/static-constructor-equivalent-in-objective-c); but once created it doesn't go away. – David Gelhar Oct 28 '10 at 03:05
  • Consider using GCD's dispatch_semaphore_create instead of NSLock. These can be locked and unlocked from any thread, unlike NSLock which has to be used on the same thread always. – bandejapaisa May 10 '13 at 10:44
  • @DavidGelhar according to @JeremyP in a comment on the question, you should synchronize on `[MyClass class]`, not `[self class]`. It seems @JeremyP is saying `[self class]` will return a different object each time, not a singleton. – Liron Yahdav Apr 04 '14 at 18:04
  • @Liron what JeremyP is saying is not that `[self class]` returns a different object each time. He's saying that if you have a subclass MySubclass that inherits from MyClass, `[self class]` will return `[MySubclass class]` when called from an instance of `MySubclass`. It's a valid point. – David Gelhar Apr 05 '14 at 03:23
2

A response 4 years later presently in iOS8. :o) The best for me is to use a singleton class as follows :

(yourFile.h)

#import <Foundation/Foundation.h>

@interface singletonSessionId : NSObject

+ (singletonMsgNbr*)sharedSingleton;

- (void)generateSessionId;

@property NSInteger value;

@end

===================================================== (yourFile.m)

#import "singletonSessionId.h"

@implementation singletonSessionId
@synthesize value = _value;

+ (singletonMsgNbr*)sharedSingleton {

    static singletonSessionId *instance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[singletonSessionId alloc]init];
        instance.value = 1000;
    });

    return instance;
}

- (void)generateSessionId {
    _value += 1;
}

@end

You just need to call the method 'generateSessionId' for each new Id value. Using this class to generate your session Ids should be far enough I think.

Hope it will help the readers of this post. :o)

EDIT

As stated by some readers, the NSInteger 'value' isn't thread safe following this way, only the singleton is.

To obtain a complete thread safe 'value' instance variable just change its NSInteger type into NSNumber (to be converted in 'int' with appropriate methods) that is completely thread safe according to Apple's documentation.

This may be the simplest and fastest way to get what is desired.

XLE_22
  • 5,124
  • 3
  • 21
  • 72
  • @ondermerol : indeed and that's exactly what it's meant to do => 'You just need to call the method 'generateSessionId' for each new Id value'. – XLE_22 Jun 15 '15 at 08:12
  • Sorry for the late answer, it works like a charm, I made a mistake only. – ondermerol Jun 22 '15 at 14:02
  • 2
    @XLE_22 : Is this thread safe? Suppose thread1 calls generateSessionID, then thread2 calls generateSessionID before thread1 has had a chance to retrieve the value? Wouldn't they both be using the same id? And if they both call the method at the same time, isn't there potential for memory corruption since no synchronization tool is being used? – jk7 Jan 08 '16 at 19:07
  • 1
    @jk7 dispatch_once will block all calls until one of them succeeds. The docs say "If called simultaneously from multiple threads, this function waits synchronously until the block has completed." In fact, the method was specifically designed for this use case: "This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block." – Bjorn Roche Jan 11 '17 at 20:06
  • 1
    This is not thread-safe for exactly the reason that @jk7 states. The dispatch_once has nothing to do with it, because it is not called when calling generateSessionId – fishinear Feb 27 '17 at 16:19
  • 1
    @jk7 : 'fishinear' is right. At that time, I thought having a perfect thread safe singleton was enough to generate and increment a variable in a thread safe way. That's definitely not the case. In my view, the best way to read and write an instance variable with no race condition is to use the 'dispatch_barriers' feature provided by Grand Central Dispatch. Few things to be implemented but a result that will be 100% sure. – XLE_22 Mar 20 '18 at 08:31
1

There are many options, including (from high-level to low-level) the @synchronized Objective-C directive, NSLock, pthread_mutex_lock, and atomic operations.

Read the "Synchronization" section of the Threading Programming Guide for details.

David Gelhar
  • 27,873
  • 3
  • 67
  • 84