Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

make a simple NSInteger counter thread safe

I define a NSInteger counter and updated its value in a callback like the following code shows (callback is in another thread):

-(void) myFunc {
  NSLog(@"initialise counter...");
  // I try to use volatile to make it thread safe
  __block volatile NSInteger counter = 0;

  [self addObserver:myObserver withCallback:^{
     // this is in another thread
     counter += 1;
     NSLog(@"counter = %d", counter);
  }];
}

I use volatile keyword to make the counter thread safe, it is accessed in a callback block which belongs to another thread.

When I invoke myFunc two times:

// 1st time call
[self myFunc];
// 2nd time call
[self myFunc];

the output is like this:

initialise counter...
counter = 1;
counter = 2;
counter = 3;
counter = 4;
counter = 1; // weird
initialise counter...
counter = 2; // weird
counter = 3;
counter = 1; // weird
counter = 4;

It looks like the 2nd time call produce a counter with wrong initial value, and the output before counter=4 is counter=1 which is also weird.

Is it because my code is not thread safe even with volatile keyword? If so, how to make my counter thread safe? If it is thread safe, why I get weird output?

like image 771
Leem.fin Avatar asked Oct 12 '25 22:10

Leem.fin


1 Answers

For the simple case of an atomic counter, GCD is overkill. Use the OSAtomic functions:

-(void) myFunc {
  static int64_t counter;

  [self addObserver:myObserver withCallback:^{
     // this is in another thread
     int64_t my_value = OSAtomicIncrement64Barrier(&counter);
     NSLog(@"counter = %d", my_value);
  }];
}

Note that the code logs the result of the increment function rather than the static variable. The result gives you the atomic result of your specific operation. Using the static variable would give you a snapshot of the counter that's not atomic with respect to your increment operation.

like image 183
Ken Thomases Avatar answered Oct 14 '25 19:10

Ken Thomases