Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dispatch once (dispatch_once) singleton freezes / locks in objective c

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:

  • Code execution stops on using thread safe Singleton initialization code
  • http://cocoasamurai.blogspot.jp/2011/04/singletons-your-doing-them-wrong.html
  • http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
  • ios singleton class crashes my app
  • http://benalpert.com/2014/04/02/dispatch-once-initialization-on-the-main-thread.html
  • http://www.bignerdranch.com/blog/dispatch_once-upon-a-time/

Thanks for your help!

like image 353
Marcel Gruber Avatar asked Feb 18 '15 23:02

Marcel Gruber


1 Answers

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.

like image 98
Alex Curylo Avatar answered Oct 22 '22 13:10

Alex Curylo