Created
December 5, 2013 22:31
-
-
Save phausler/7815194 to your computer and use it in GitHub Desktop.
A simple CFRunLoopSource based atomic queue for block execution in a header implementation (poor man's GCD)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _CFBLOCKQUEUE_H_ | |
#define _CFBLOCKQUEUE_H_ | |
#include <Block.h> | |
#include <libkern/OSAtomic.h> | |
#include <CoreFoundation/CFString.h> | |
#include <CoreFoundation/CFSet.h> | |
#include <CoreFoundation/CFRunLoop.h> | |
typedef void (^CFBlockQueueBlock)(void); | |
typedef struct CFBlockQueueElement { | |
#if __has_feature(objc_arc) | |
void *block; | |
#else | |
CFBlockQueueBlock block; | |
#endif | |
struct CFBlockQueueElement *next; | |
} CFBlockQueueElement; | |
typedef volatile struct { | |
void *head; | |
void *tail; | |
OSSpinLock lock; | |
} CFBlockQueueList; | |
typedef struct { | |
CFRunLoopSourceRef source; | |
CFBlockQueueList list; | |
CFMutableSetRef modes; | |
int32_t count; | |
Boolean signaled; | |
} CFBlockQueue; | |
void CFBlockAtomicEnqueue(CFBlockQueueList *list, void *elt, size_t offset) { | |
OSSpinLockLock(&list->lock); | |
if (list->tail == NULL) { | |
list->tail = elt; | |
list->head = elt; | |
} else { | |
*((void **)((char *)list->tail + offset)) = elt; | |
list->tail = elt; | |
} | |
OSSpinLockUnlock(&list->lock); | |
} | |
void *CFBlockAtomicDequeue(CFBlockQueueList *list, size_t offset) { | |
void *head = NULL; | |
OSSpinLockLock(&list->lock); | |
head = list->head; | |
if (head != NULL) { | |
void **next = (void **)((char *)head + offset); | |
list->head = *next; | |
if (list->head == NULL) { | |
list->tail = NULL; | |
} | |
} | |
OSSpinLockUnlock(&list->lock); | |
return head; | |
} | |
static inline void CFBlockQueueInit(CFBlockQueue *q) { | |
q->list.head = NULL; | |
q->list.tail = NULL; | |
q->list.lock = OS_SPINLOCK_INIT; | |
q->modes = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); | |
q->count = 0; | |
} | |
static inline void CFBlockQueueDispose(CFBlockQueue *q) { | |
if (q->source) { | |
CFRunLoopSourceInvalidate(q->source); | |
} | |
CFRelease(q->modes); | |
CFBlockQueueElement *elt = NULL; | |
while ((elt = (CFBlockQueueElement *)CFBlockAtomicDequeue(&q->list, offsetof(struct CFBlockQueueElement, next))) != NULL) { | |
free(elt); | |
} | |
} | |
static inline void CFBlockQueueEnqueue(CFBlockQueue *q, CFBlockQueueBlock block) { | |
CFBlockQueueElement *elt = malloc(sizeof(CFBlockQueueElement)); | |
#if __has_feature(objc_arc) | |
elt->block = (__bridge_retained void *)block; | |
#else | |
elt->block = Block_copy(block); | |
#endif | |
q->count = OSAtomicIncrement32(&q->count); | |
CFBlockAtomicEnqueue(&q->list, elt, offsetof(struct CFBlockQueueElement, next)); | |
if (q->source != NULL && !q->signaled) { | |
q->signaled = true; | |
CFRunLoopSourceSignal(q->source); | |
} | |
} | |
static inline CFBlockQueueBlock CFBlockQueueDequeue(CFBlockQueue *q) { | |
CFBlockQueueBlock block = NULL; | |
CFBlockQueueElement *elt = (CFBlockQueueElement *)CFBlockAtomicDequeue(&q->list, offsetof(struct CFBlockQueueElement, next)); | |
if (elt != NULL) { | |
#if __has_feature(objc_arc) | |
block = (__bridge CFBlockQueueBlock)elt->block; | |
#else | |
block = elt->block; | |
#endif | |
q->count = OSAtomicDecrement32(&q->count); | |
free(elt); | |
} | |
return block; | |
} | |
static inline void queue_schedule(CFBlockQueue *q, CFRunLoopRef rl, CFStringRef mode) { | |
} | |
static inline void queue_cancel(CFBlockQueue *q, CFRunLoopRef rl, CFStringRef mode) { | |
} | |
static inline void queue_perform(CFBlockQueue *q) { | |
void (^block)(void) = NULL; | |
#if __has_feature(objc_arc) | |
while ((block = CFBlockQueueDequeue(q)) != NULL) { | |
#else | |
while ((block = CFBlockQueueDequeue(q)) != NULL) { | |
#endif | |
block(); | |
#if __has_feature(objc_arc) | |
CFBridgingRelease((__bridge CFTypeRef)block); | |
#else | |
Block_release(block); | |
#endif | |
block = NULL; | |
} | |
q->signaled = false; | |
} | |
static inline void CFBlockQueueSchedule(CFBlockQueue *q, CFRunLoopRef rl, CFStringRef mode) { | |
CFSetAddValue(q->modes, mode); | |
if (q->source == NULL) { | |
CFRunLoopSourceContext ctx = { | |
.version = 0, | |
.info = (void *)q, | |
.schedule = (void (*)(void *, CFRunLoopRef, CFStringRef))&queue_schedule, | |
.cancel = (void (*)(void *, CFRunLoopRef, CFStringRef))&queue_cancel, | |
.perform = (void (*)(void *))&queue_perform | |
}; | |
q->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx); | |
CFRunLoopAddSource(rl, q->source, mode); | |
CFRelease(q->source); | |
if (q->count > 0) { | |
CFRunLoopSourceSignal(q->source); | |
} | |
} else { | |
CFRunLoopAddSource(rl, q->source, mode); | |
} | |
} | |
static inline void CFBlockQueueUnschedule(CFBlockQueue *q, CFRunLoopRef rl, CFStringRef mode) { | |
CFRunLoopRemoveSource(rl, q->source, mode); | |
CFSetRemoveValue(q->modes, mode); | |
if (CFSetGetCount(q->modes) == 0) { | |
q->source = NULL; | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment