Merge pull request #1397 from maicki/ASRunLoopQueueContinueProcessing

[ASRunLoopQueue] Add custom run loop source to signal if jobs are still enqueued, to guarantee another runloop turn.
This commit is contained in:
appleguy 2016-03-18 14:48:50 -07:00
commit 0745eabec9

View File

@ -9,11 +9,17 @@
#import "ASRunLoopQueue.h" #import "ASRunLoopQueue.h"
#import "ASThread.h" #import "ASThread.h"
#import <cstdlib>
#import <deque> #import <deque>
static void runLoopSourceCallback(void *info) {
// No-op
}
@interface ASRunLoopQueue () { @interface ASRunLoopQueue () {
CFRunLoopRef _runLoop; CFRunLoopRef _runLoop;
CFRunLoopObserverRef _runLoopObserver; CFRunLoopObserverRef _runLoopObserver;
CFRunLoopSourceRef _runLoopSource;
std::deque<id> _internalQueue; std::deque<id> _internalQueue;
ASDN::RecursiveMutex _internalQueueLock; ASDN::RecursiveMutex _internalQueueLock;
} }
@ -36,12 +42,26 @@
}; };
_runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock); _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock);
CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); 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 = (CFRunLoopSourceContext *)calloc(1, sizeof(CFRunLoopSourceContext));
runLoopSourceContext->perform = runLoopSourceCallback;
_runLoopSource = CFRunLoopSourceCreate(NULL, 0, runLoopSourceContext);
CFRunLoopAddSource(runloop, _runLoopSource, kCFRunLoopCommonModes);
free(runLoopSourceContext);
} }
return self; return self;
} }
- (void)dealloc - (void)dealloc
{ {
if (CFRunLoopContainsSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes)) {
CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes);
}
CFRelease(_runLoopSource);
_runLoopSource = nil;
if (CFRunLoopObserverIsValid(_runLoopObserver)) { if (CFRunLoopObserverIsValid(_runLoopObserver)) {
CFRunLoopObserverInvalidate(_runLoopObserver); CFRunLoopObserverInvalidate(_runLoopObserver);
} }
@ -103,6 +123,9 @@
if (!foundObject) { if (!foundObject) {
_internalQueue.push_back(object); _internalQueue.push_back(object);
CFRunLoopSourceSignal(_runLoopSource);
CFRunLoopWakeUp(_runLoop);
} }
} }