1

I have come across a very interesting problem related to queue dead lock in iOS. Any way to avoid this?

Consider this:

  1. Create a custom serial queue.
  2. Dispatch some task (#1) on this serial queue asynchronously.
  3. This async task (#1) on dispatches some task (#2) onto main queue sync.
  4. Main queue dispatches some task (#3) onto serial queue sync.
  5. Result - DeadLock

Below is the sample code for this.

  • Since self.opQueue is a serial queue, task#3 will not start till task#1 completes.
  • Since task#1 is calling main queue sync, so it will never complete till main queue completes task#2.
  • Since main queue is waiting for opQueue to finish task#3, and opQueue is waiting for main queue to finish task#2 there is a deadlock.

    #import "ViewController.h"
    
    @interface ViewController ()
    @property(nonatomic,strong) dispatch_queue_t opQueue;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
    
      dispatch_queue_t queue =  dispatch_queue_create("com.tarun.sqqueue",
                                                   DISPATCH_QUEUE_SERIAL);
      self.opQueue = queue;
    
      [self performOperations];
    }
    
    
    /// 1. Dispatch on serial queue async.
    /// 2. This async task on serial queue dispatchs some task onto 
    ///    main queue sync.
    /// 3. Main queue dispatched some task onto serial queue sync.
    /// 4. Result - DeadLock
    - (void)performOperations {
    
      /// task#1: Dispatch task on the serial Queue Asynchronously.
      /// So this is not blocking.
      dispatch_async(self.opQueue, ^{
        for (int i = 1; i<=100; i++) {
          NSLog(@"%d. Async on Serial Queue from Main Queue.",i);
        }
    
        /// task#2: Dispatching task on main queue synchronously from serial
        /// queue.So this queue will wait till main queue executes this task.(Blocking)
        dispatch_sync(dispatch_get_main_queue(), ^{
          for (int i = 1; i<=100; i++) {
            NSLog(@"%d. Sync on main queue from Serial Queue.",i);
          }
        });
      });
    
      /// task#3: Dispatching task on swrial queue synchronously from main
      /// queue.So main queue will wait till serial queue executes this task. (Blocking)
      dispatch_sync(self.opQueue, ^{
        for (int i = 1; i<=100; i++) {
          NSLog(@"%d. Sync on Serial Queue From Main Queue.",i);
        }
      });
    
      /// Since self.opQueue is a serial queue, task#3 will not start till task#1 completes.
      /// Since task#1 is calling main queue sync, 
      /// so it will never complete till main queue completes task#2.
      /// Since main queue is waiting for opQueue to finish task#3, and opQueue is waiting for main queue to finish task#2 there is a deadlock.
    
      NSLog(@"Back to main queue");
    }
    
    
    @end
    
4
  • Yes, don't do this. In particular dispatching sync tasks on serial queues that dispatch other tasks synchronously is asking for trouble unless you know which queues you are using Commented Jul 21, 2017 at 9:16
  • Here the Asynchronous task is dispatching synchronously. But I am not dispatching to the same queue synchronously ever. And let's say if custom serial queue is a part of a framework/library and exposed to be used by the app. Then framework doesn't have control over it, so how to avoid this? Commented Jul 21, 2017 at 9:21
  • But you are dispatching to the same queue; not directly, but that doesn't matter. It would generally not be the case that a framework would expose a queue for use by others. A framework may use a queue internally to manage a critical section or may accept a queue from the caller, but it would be a bad idea for a framework to provide a queue Commented Jul 21, 2017 at 9:23
  • Yes it is a hypothetical situation and ideally a framework would never expose the queue to used by outsiders but still theoretically it is possible and very easy to get into this deadlock. So I was just wondering it there any way we can avoid this. Commented Jul 21, 2017 at 9:42

1 Answer 1

1

From Apple

Important: You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.

Its a guaranteed dead lock. The only way to avoid this is to not implement it this way.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.