K
K
Krypt2013-11-05 18:39:54
iOS
Krypt, 2013-11-05 18:39:54

Is [NSMutableArray count] thread-safe?

There is a code. Stranger. (in both senses, yes).
There is a flow in which a cycle is organized by hands.
Literally, while (running) { }. There are no NSRunLoops, of course. You can, of course, insert about proper perversion, but you don’t want to.
There was a need to perform operations in its context, while not killing the performance (the loop counts the game logic, among other things it executes the lua machine and draws an opengl frame. 60 fps and all that)
As a result, the following solution was born:

while (_running) {
        if ([tasks count] > 0)
        {
            NSArray *tasksCopy = nil;
            
            @synchronized(tasks)
            {
                tasksCopy = [tasks copy];
                [tasks removeAllObjects];
            }
            
            for (NSInvocation *invocation in tasksCopy)
            {
                [invocation invoke];
            }
            
            [tasksCopy release];
        }
    
        <...>
    }

Tasks, of course, are added from another thread.
How correct is such a call to [NSMutableArray count]?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
C
corristo, 2013-11-05
@Krypt

Judging by the source code, there is simply a return of the structure field, without side effects. If I'm not mistaken, for a variable whose size is equal to a machine word, the second thread will always see a valid state (if the array length were 64-bit in 32-bit runtime, there would be a case when another thread would see 4 bytes of the current value and 4 previous). Source CF( Mutable
)ArrayRef : www.opensource.apple.com/source/CF/CF-476.10/CFArray.c /Reference/OSAtomic_header_reference/Reference/reference.html (see OSAtomicDequeue and OSAtomicEnqueue methods).

Y
Yan169, 2013-11-06
@Yan169

  1. corristo wrote everything correctly , it is non-thread-safe NSMutableArray, but specifically, the method countcertainly returns the value atomically, which, however, does not mean thread-safety, i.e. for example code
    if ([tasks count] > 0){
        [tasks removeObjectAtIndex:0];
    }
    

    may throw an exception (for example, the thread adding the element managed to increment countbut did not completely complete adding the element by the time the call was made removeObjectAtIndex:).
    But, as you rightly noted, nothing terrible should happen specifically in your code.
    However, as I see it, moving if ([tasks count] > 0)outside the synchronization block is premature optimization, there are other places in this code that you should pay attention to in the first place.
  2. The loop while (running){ }will, in the absence of tasks, constantly load the processor with “empty” cycles, when, as normal event loop, it does not.
  3. NSRunLoopYou may not need a full- fledged one, but for NSAutoreleasePoolsure. Moreover, since NSAutoreleasePoolclears itself at the end of each event loop that you don't have, you need to periodically create and clear NSAutoreleasePoolit inside the while(running).
  4. The choice NSInvocationfor assignments is somewhat surprising. Firstly, it is inconvenient, it is much more convenient to use blocks, and secondly, it is relatively slow (which, in principle, is probably not critical, it must be profiled). Calling one block takes about the same time as sending one message, and the call NSInvocationis ~20 times slower. True, if you use weak variables in the block , it will be something like NSInvocation, but this is not your case, because not used by ARC .

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question