Reputation: 2848
I am using the PDFKit Framework into my cocoa app for PDFViewer.When I am trying to delete one of the page from the PDFDocument The app freezes at the line of code
[[self pdfDocument] removePageAtIndex:0]; // can see this Problem only in Mac OS X 10.11
This works perfectly when I run the app in Mac OS X 10.10
I read all the related apple documents but I didn't get any solution yet.
Here is the backtrace :
* thread #1: tid = 0x85e1, 0x00007fff92571f5e libsystem_kernel.dylib`__psynch_cvwait + 10, queue = 'com.apple.main-thread', stop reason = signal SIGTERM
frame #0: 0x00007fff92571f5e libsystem_kernel.dylib`__psynch_cvwait + 10
frame #1: 0x00000001006c05f7 libsystem_pthread.dylib`_pthread_cond_wait + 767
frame #2: 0x00007fff904c6e32 Foundation`-[__NSOperationInternal _waitUntilFinished:] + 131
frame #3: 0x00007fff904921fa Foundation`-[NSOperationQueue waitUntilAllOperationsAreFinished] + 254
* frame #4: 0x000000010017efe1 Neat`-[NRMPDFCoordinator waitUntilAllOperationsAreFinished](self=0x0000608000ad3be0, _cmd=0x00007fff8877e285) + 145 at NRMPDFCoordinator.m:1362
frame #5: 0x00000001000109cf Neat`-[NRMItemEditorDocument saveDocumentWithDelegate:didSaveSelector:contextInfo:](self=0x000060000094a190, _cmd=0x00007fff88777581, delegate=0x0000000000000000, didSaveSelector=0x0000000000000000, contextInfo=0x0000000000000000) + 1151 at NRMItemEditorDocument.m:325
frame #6: 0x000000010001018a Neat`-[NRMItemEditorDocument saveDocument:](self=0x000060000094a190, _cmd=0x00007fff8874cbb4, sender=0x00006080003a8b20) + 58 at NRMItemEditorDocument.m:234
frame #7: 0x0000000100013bef Neat`-[NRMItemEditorWindowController saveAndClose:](self=0x00006080003a8b20, _cmd=0x00000001002cf2d2, sender=0x000060000094a5b0) + 95 at NRMItemEditorWindowController.m:244
frame #8: 0x00007fff87068082 libsystem_trace.dylib`_os_activity_initiate + 75
frame #9: 0x00007fff87fc79b5 AppKit`-[NSApplication sendAction:to:from:] + 460
frame #10: 0x00007fff87fd9bb2 AppKit`-[NSControl sendAction:to:] + 86
frame #11: 0x00007fff87fd9adc AppKit`__26-[NSCell _sendActionFrom:]_block_invoke + 131
frame #12: 0x00007fff87068082 libsystem_trace.dylib`_os_activity_initiate + 75
frame #13: 0x00007fff87fd9a39 AppKit`-[NSCell _sendActionFrom:] + 144
frame #14: 0x00007fff87068082 libsystem_trace.dylib`_os_activity_initiate + 75
frame #15: 0x00007fff87fd805e AppKit`-[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2693
frame #16: 0x00007fff88020d1c AppKit`-[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 744
frame #17: 0x00007fff87fd6788 AppKit`-[NSControl mouseDown:] + 669
frame #18: 0x00007fff88524575 AppKit`-[NSWindow _handleMouseDownEvent:isDelayedEvent:] + 6322
frame #19: 0x00007fff88525559 AppKit`-[NSWindow _reallySendEvent:isDelayedEvent:] + 212
frame #20: 0x00007fff87f6ad31 AppKit`-[NSWindow sendEvent:] + 517
frame #21: 0x00007fff87eeaccb AppKit`-[NSApplication sendEvent:] + 2540
frame #22: 0x0000000100225f35 Neat`-[NRMApplication sendEvent:](self=0x00006000001205a0, _cmd=0x00007fff88749e04, event=0x0000608000725640) + 1141 at NRMApplication.m:95
frame #23: 0x00007fff87d51f3e AppKit`-[NSApplication run] + 796
frame #24: 0x00007fff87d1b162 AppKit`NSApplicationMain + 1176
frame #25: 0x0000000100012d67 Neat`main(argc=3, argv=0x00007fff5fbff718) + 119 at main.m:21
frame #26: 0x0000000100001e74 Neat`start + 52
here is the method where I am using removePageAtIndex method of PDFDocument
-(NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSLog(@"\n Inside removePageOpImpl Method ...");
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
NSLog(@"Page count: %ld", [self pageCount]);
if( index < [self pageCount] )
{
NSLog(@"PDF Document:-- %@", [self pdfDocument]);
NSLog(@"Index is: %ld", index);
@try {
[(PDFDocument *)[self pdfDocument] removePageAtIndex:index];//At this line the app getting freezed and control is ended.
NSLog(@"Page count after delete: %ld", [self pageCount]);
}
@catch (NSException *exception) {
NSLog(@"Exception: %@", exception);
}
@finally {
NSLog(@"Finally called");
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
}
}
else
{
// TODO: error
}
}
return error;
}
Can anyone please suggest me what might be the problem... also attached screenshots of the Queues which were blocking the UI
I tried applying dispatch_async on main queue to the PDFDocument page removal operations like below
- (NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
if( index < [self pageCount] )
{
dispatch_async(dispatch_get_main_queue(), ^{
[[self pdfDocument] removePageAtIndex:index];
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
});
}
else
{
// TODO: error
}
}
return error;
}
Now the app is not hanging, but I fall into another problem. I have other operations which should run synchronously after the removePageOpImpl operations. but they are executing before removePageOpImpl completion which is changing the behaviour of my app. Can you suggest me how can I execute other operations synchronously after removePageOpImpl. I read about completion handler but, in this scenario I am confused of how to use it.
Please suggest
Upvotes: 4
Views: 218
Reputation: 28409
You have created a nice little deadlock here.
Your main thread is waiting for an operation to finish, but that operation (indicated by this trace)...
is running on a background thread and, as you can see, it's waiting on a semaphore that is most likely only going to be signaled from the main thread. This has the earmarks of something like dispatch_sync
to the main thread. Since it happens inside the PDFDocument
implementation, I imagine it is trying to make sure it runs something on the main thread before returning to the user (reader/writer lock
Thus, your main thread is waiting for an operation to finish, and that operation is waiting for the main thread to finish what it's trying to do. Classic deadlock.
This happens because the main thread kicks off the save, and then waits for everything to complete before progressing (but some of that other work also needs to run on the main thread).
You need to kick off the save as an asynchronous operation, so it is not running on the main thread while waiting for the operations to complete. Then, when the save is done, if you have to update UI, it can report its success/failure via a completion block or delegate running on the main thread.
Upvotes: 2