2

I'm new to GCD, and what seems to be a simple use of it doesn't work for me. I have the following code:

+ (void)synchronizationTimerFired:(NSTimer *)theTimer
{
    if ((synchronizationUpNeededFlag) || (synchronizationDownNeededFlag))
    {
        if ((!synchronizationUpInProgressDepthQuantity) && (!synchronizationDownInProgressDepthQuantity))
        {
            dispatch_queue_t synchronizationQueue = dispatch_queue_create("synchronizationQueue",NULL);
            dispatch_async(synchronizationQueue, ^(void) {

                NSLog(@"Top");
                ...code...
                ...code...
                ...code...
                NSLog(@"Bottom");

            });
        }
    }

    // Check if there is no timer, or if it is not currently valid,
    // and yet if synchronization is turned on,
    // then establish a repeating timer to attend to synchronization related matters.
    if ((!synchronizationTimer) || (!synchronizationTimer.isValid))
    {
        if (synchronizationOnFlag)
        {
            synchronizationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(synchronizationTimerFired:) userInfo:nil repeats:YES];
        }
    }
}

The log reads "Top" and nothing else. The code in the middle doesn't have an endless loop- it just never executes all the way through. I can put breakpoints in the code in the middle and there's a point where the program execution will break, and after which it won't. And there's a point right in the middle where sometimes the execution will stop at the breakpoint and other times it doesn't.

It seems to me as though the synchronizationQueue dispatch queue is being deallocated, but I can't call dispatch_retain because the compiler complains that dispatch_retain cannot be used in ARC. What am I missing?

In response to people asking about the code in-between, the program execution stops in this method call (represented by one of those lines of ...code...) at the line that says if (fetchArray.count), commented below.

+ (NSDate *)latestParseReceivedDownUpdatedAtDateForCoreDataEntityNameString:(NSString *)coreDataEntityNameString
{
    NSDate *functionReturnValue = nil;

    // Create fetchRequest
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:coreDataEntityNameString];

    // Set sort descriptor
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"parseReceivedDownUpdatedAtDate" ascending:NO]]];

    // We are only interested in one result
    [fetchRequest setFetchLimit:1];

    // Execute fetchRequest
    NSError *fetchError = nil;
    NSArray *fetchArray = [JBSAPPDELEGATE.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];

    if (fetchArray == nil)
    {
        NSLog(@"Unresolved error %@, %@", fetchError, [fetchError userInfo]);
        abort();
    }

    // If there are any records at all in our persistent store, we'll have exactly one.
    // But that doesn't mean it won't be nil, as if that record has never come down from
    // parse it will be a nil date on the managed object.
    if (fetchArray.count) // PROGRAM EXECUTION STOPS EITHER HERE, OR JUST BEFORE HERE
    {
        NSManagedObject *managedObject = [fetchArray objectAtIndex:0];
        functionReturnValue = [managedObject valueForKey:@"parseReceivedDownUpdatedAtDate"];
    }

    return functionReturnValue;
}

I will add that if I simply comment out the call to dispatch_async that everything executes fine. It just executes on the main thread, which I'd rather have it not do.

John Bushnell
  • 1,851
  • 22
  • 29
  • 1
    I think you should post the code between the two `NSLog`. – Gabriele Petronella Nov 02 '13 at 23:18
  • The queue is not being deallocated, at least not by the code shown, until the async call finishes. Replacing the code in the middle with `sleep(2);`, and even adding `synchronizationQueue=nil;` after the block will execute just fine. So double-check your other code. – Gerd K Nov 02 '13 at 23:20
  • The above code is a simplification. The code in between is actually somewhat long. I'm trying not to belabor the question. The actual code reads: dispatch_queue_t synchronizationQueue = dispatch_queue_create("synchronizationQueue",NULL); dispatch_async(synchronizationQueue, ^(void) { [self synchronizeAnythingNeeded]; }); – John Bushnell Nov 02 '13 at 23:20
  • 1
    @John I understand but there's nothing wrong with the code you posted so far, so it's very likely that the issue is in the part you omitted. – Gabriele Petronella Nov 02 '13 at 23:23
  • @John Listen to the man, post the code between the `NSLog`s – IluTov Nov 02 '13 at 23:26
  • I've posted the code from the method call within the execution block where the failure takes place. If you still want more ask and I'll give it, but it'll be a lot of code. – John Bushnell Nov 02 '13 at 23:35
  • AFAIK if you want to use `-executeFetchRequest:` in the background, you need a separate context. – Gerd K Nov 02 '13 at 23:45
  • @Gerd K Hmm. I've seen people use a separate context in background code before. Perhaps that's the problem. What context would I use though? – John Bushnell Nov 02 '13 at 23:47
  • Relevant: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html – Gabriele Petronella Nov 02 '13 at 23:48

2 Answers2

4

Is your managedObjectContext an NSManagedObjectContext? If so, did you create a managed object context on the specific thread that you are using for your dispatch queue? If you read the docs on NSManagedObjectContext, it says:

...a context assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method. You should not, therefore, initialize a context on one thread then pass it to a different thread. Instead, you should pass a reference to a persistent store coordinator and have the receiving thread/queue create a new context derived from that.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • 1
    I believe this is the answer, as I'm using a managedObjectContext created on the main thread. It's a certainly a problem, so for now I'm marking this as answered. The thread for the dispatch queue is presumably terminating due to accessing a managedObjectContext not created on the current thread. Thank you, and thanks to Gabriele Petronella who posted the developer doc link in the comments on the main post. – John Bushnell Nov 02 '13 at 23:59
0

I bet you created yourself a deadlock somewhere.

Use the debugger. Check what every thread is doing. You'll probably find some thread that you think should be continuing and isn't.

gnasher729
  • 51,477
  • 5
  • 75
  • 98