diff --git a/AsyncDisplayKit/ASRunLoopQueue.mm b/AsyncDisplayKit/ASRunLoopQueue.mm index 2eb3420e18..b27a745279 100644 --- a/AsyncDisplayKit/ASRunLoopQueue.mm +++ b/AsyncDisplayKit/ASRunLoopQueue.mm @@ -11,9 +11,14 @@ #import +static void runLoopSourceCallback(void *info) { + // No-op +} + @interface ASRunLoopQueue () { CFRunLoopRef _runLoop; CFRunLoopObserverRef _runLoopObserver; + CFRunLoopSourceRef _runLoopSource; std::deque _internalQueue; ASDN::RecursiveMutex _internalQueueLock; } @@ -36,12 +41,24 @@ }; _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock); CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); + + // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of + // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done + CFRunLoopSourceContext runLoopSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &runLoopSourceCallback}; + _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext); + CFRunLoopAddSource(runloop, _runLoopSource, kCFRunLoopCommonModes); } return self; } - (void)dealloc { + if (CFRunLoopContainsSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes)) { + CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + } + CFRelease(_runLoopSource); + _runLoopSource = nil; + if (CFRunLoopObserverIsValid(_runLoopObserver)) { CFRunLoopObserverInvalidate(_runLoopObserver); } @@ -103,6 +120,9 @@ if (!foundObject) { _internalQueue.push_back(object); + + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); } }