9
votes

This line of code is being called in my awakeFromFetch method located inside a custom managed object which implements NSManagedObject. This line in particular is making a call to my singleton network manager class called sharedManager.

[self setSync:(![[WKNetworkManager sharedManager] objectHasPendingRequests:self.objectID]) ];

The dispatch_once block will be hit as is shown below. Notice that it is implemented in a good way as is shown here:

enter image description here

The dispatch_once call then goes to once.h and here it freezes right on the highlighted line:

dipatch_once deeper

Here is the stack trace:

stack trace

All of this happens when trying to load a previously saved network queue file. The application is closed fully to save, and then launched again and then this freezing/locking occurs.

I even tried to use this code instead to solve the problem as is suggested here, and it did not work. But changing this would probably not matter anyway, as my original dispatch_once code has been working just fine for a long time. It's just in this particular case.

if ([NSThread isMainThread]) 
{
    dispatch_once(&onceToken, ^{
    stack = [[KACoreDataStack alloc] init];});
} 
else 
{
    dispatch_sync(dispatch_get_main_queue(), ^{
    dispatch_once(&onceToken, ^{
    stack = [[KACoreDataStack alloc] init];});
});
}

So far, these are my sources for troubleshooting this type of problem:

Thanks for your help!

2

2 Answers

6
votes

Damn son, that is one fine job of question-asking.

The problem here is that recursive calls to dispatch_once will deadlock. If you want exact details, you can find them with the source here. So you need to rewrite to avoid that.

My opinion is that there is an architectural misstep with whatever leads to this -loadQueueFromDisk call. You absolutely do not want to be doing anything that is as potentially unbounded in execution time as going off and loading something from disk inside a dispatch_once. What you want to be doing is the absolute minimum to create an addressable singleton and get back out. Then, give it an internal state of "Initializing", and ship the disk-loading stuff onto a non-blocking queue somewhere. That way everything that doesn't depend on the stuff loaded from disk can carry on, and everything that does depend on the stuff loaded from disk can add itself to the queue the disk loading is on, or check back every few hundred milliseconds to see if it's in an "Initialized" state yet, or something along those lines.

0
votes

Thanks to the help of alexcurylo, queueing out the loadFromDisk job addressed the issue. In order so that people aren't confused, it just so happens that with this code below I am queuing a job that loads an array (which acts as a saved queue of user requests) from the disk. You could be queueing any job, such as loading images / etc. The following code in my sharedManager class (where load from disk is being called) worked, as is shown here:

-(void) loadQueueFromFileByQueueing
{
  //Create a Queue
  /* Note that fileStrQueue should be added to the .h file as NSOperationQueue *fileStrQueue;*/
  fileStrQueue = [[NSOperationQueue alloc] init];

  //Create an  operation which will invoke method loadQueueFromDisk
  NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadQueueFromDisk) object:nil];

  //Add this operation to the queue
  [fileStrQueue addOperation:operation];

  //Releasing the above operation as it is already retained by the queue.
  // [operation release]; // If you are using ARC, this won't be necessary
}