Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I know I'm late to the party with this, but I've found a strange combination of circumstances that <code>@synchronized</code> handles poorly and is probably responsible for your problem. I don't have a solution to it, besides to change the code to eliminate the cause once you know what it is.</p> <p>I will be using this code below to demonstrate.</p> <pre><code>- (int)getNumberEight { @synchronized(_lockObject) { // Point A return 8; } } - (void)printEight { @synchronized(_lockObject) { // Point B NSLog(@"%d", [self getNumberEight]); } } - (void)printSomethingElse { @synchronized(_lockObject) { // Point C NSLog(@"Something Else."); } } </code></pre> <p>Generally, <code>@synchronized</code> is a recursively-safe lock. As such, calling <code>[self printEight]</code> is ok and will not cause deadlocks. What I've found is an exception to that rule. The following series of events will cause deadlock and is extremely difficult to track down.</p> <ol> <li>Thread 1 enters <code>-printEight</code> and acquires the lock.</li> <li>Thread 2 enters <code>-printSomethingElse</code> and attempts to acquire the lock. The lock is held by Thread 1, so it is enqueued to wait until the lock is available and blocks.</li> <li>Thread 1 enter <code>-getNumberEight</code> and attempts to acquire the lock. The lock is held already and someone else is in the queue to get it next, so Thread 1 blocks. Deadlock.</li> </ol> <p>It appears that this functionality is an unintended consequence of the desire to bound starvation when using <code>@synchronized</code>. The lock is only recursively safe when no other thread is waiting for it.</p> <p>The next time you hit deadlock in your code, examine the call stacks on each thread to see if either of the deadlocked threads already holds the lock. In the sample code above, by adding long sleeps at Point A, B, and C, the deadlock can be recreated with almost 100% consistency.</p> <p>EDIT:</p> <p>I'm no longer able to demonstrate the previous issue, but there is a related situation that still causes issues. It has to do with the dynamic behavior of <code>dispatch_sync</code>. </p> <p>In this code, there are two attempts to acquire the lock recursively. The first calls from the main queue into a background queue. The second calls from the background queue into the main queue.</p> <p>What causes the difference in behavior is the distinction between dispatch queues and threads. The first example calls onto a different queue, but never changes threads, so the recursive mutex is acquired. The second changes threads when it changes queues, so the recursive mutex cannot be acquired. </p> <p>To emphasize, <strong>this functionality is by design</strong>, but it behavior may be unexpected to some that do not understand GCD as well as they could. </p> <pre><code>dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSObject *lock = [[NSObject alloc] init]; NSTimeInterval delay = 5; NSLog(@"Example 1:"); dispatch_async(queue, ^{ NSLog(@"Starting %d seconds of runloop for example 1.", (int)delay); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]]; NSLog(@"Finished executing runloop for example 1."); }); NSLog(@"Acquiring initial Lock."); @synchronized(lock) { NSLog(@"Acquiring recursive Lock."); dispatch_sync(queue, ^{ NSLog(@"Deadlock?"); @synchronized(lock) { NSLog(@"No Deadlock!"); } }); } NSLog(@"\n\nSleeping to clean up.\n\n"); sleep(delay); NSLog(@"Example 2:"); dispatch_async(queue, ^{ NSLog(@"Acquiring initial Lock."); @synchronized(lock) { NSLog(@"Acquiring recursive Lock."); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"Deadlock?"); @synchronized(lock) { NSLog(@"Deadlock!"); } }); } }); NSLog(@"Starting %d seconds of runloop for example 2.", (int)delay); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]]; NSLog(@"Finished executing runloop for example 2."); </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload