Skip to content

Instantly share code, notes, and snippets.

@phausler
Created December 5, 2013 22:31
Show Gist options
  • Save phausler/7815194 to your computer and use it in GitHub Desktop.
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)
#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