Last active
September 13, 2015 17:15
-
-
Save swillits/f5ba9815e67488e27a1a to your computer and use it in GitHub Desktop.
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
// | |
// AGEnqueueCoalescedBlock.h | |
// EnqueuedBlocks | |
// | |
// Created by Seth Willits on 2/26/15. | |
// Copyright (c) 2015 Araelium Group. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
//! Executes the block on the given queue immediately if no block with 'key' is running. Otherwise, this block is enqeued, | |
//! replacing any previously-enqueued block with the same 'key'. Once the running block finishes, the enqueued block executes (if there is one). | |
void AGEnqueueCoalescedBlock(void * key, dispatch_queue_t queue, void (^block)(void)); | |
//! Removes an enqueued block with the given key, preventing it from running. | |
void AGCancelEnqueuedBlock(void * key); | |
// | |
// AGEnqueueCoalescedBlock.m | |
// EnqueuedBlocks | |
// | |
// Created by Seth Willits on 2/26/15. | |
// Copyright (c) 2015 Araelium Group. All rights reserved. | |
// | |
#import "AGEnqueueCoalescedBlock.h" | |
static void AGEnqueueCoalescedBlock_Init(void); | |
static void AGEnqueue_runEnqueuedBlockForKey(void * key); | |
static void AGEnqueue_doneRunningEnqueuedBlockForKey(void * key); | |
static dispatch_queue_t __AGEnqueueQueue; | |
static NSMutableDictionary * __AGEnqueueTable; | |
static NSMutableSet * __AGEnqueueRunningSet; | |
#pragma mark - | |
#pragma mark Public | |
void AGEnqueueCoalescedBlock(void * key, dispatch_queue_t queue, void (^block)(void)) | |
{ | |
if (key == NULL) [NSException raise:NSInvalidArgumentException format:@"AGEnqueueCoalescedBlock key cannot be AGAllEnqueuedBlocks"]; | |
if (queue == nil) [NSException raise:NSInvalidArgumentException format:@"AGEnqueueCoalescedBlock queue cannot be nil"]; | |
if (block == nil) [NSException raise:NSInvalidArgumentException format:@"AGEnqueueCoalescedBlock block cannot be nil"]; | |
AGEnqueueCoalescedBlock_Init(); | |
dispatch_sync(__AGEnqueueQueue, ^{ | |
// Insert/Update entry | |
[__AGEnqueueTable setObject:@[[[block copy] autorelease, (id)queue]] forKey:[NSValue valueWithPointer:key]]; | |
// Run if needed | |
BOOL running = [__AGEnqueueRunningSet containsObject:[NSValue valueWithPointer:key]]; | |
if (!running) { | |
AGEnqueue_runEnqueuedBlockForKey(key); | |
} | |
}); | |
} | |
void AGCancelEnqueuedBlock(void * key) | |
{ | |
AGEnqueueCoalescedBlock_Init(); | |
dispatch_sync(__AGEnqueueQueue, ^{ | |
[__AGEnqueueTable removeObjectForKey:[NSValue valueWithPointer:key]]; | |
}); | |
} | |
#pragma mark - | |
#pragma mark Private | |
static void AGEnqueueCoalescedBlock_Init(void) | |
{ | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
__AGEnqueueQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); | |
__AGEnqueueTable = [[NSMutableDictionary alloc] init]; | |
__AGEnqueueRunningSet = [[NSMutableSet alloc] init]; | |
}); | |
} | |
// Can only be called from __AGEnqueueQueue and assumes | |
// there is no block already running for the given tag | |
static void AGEnqueue_runEnqueuedBlockForKey(void * key) | |
{ | |
NSArray * info = [[__AGEnqueueTable objectForKey:[NSValue valueWithPointer:key]] retain]; | |
if (info) { | |
void (^block)(void) = [info objectAtIndex:0]; | |
dispatch_queue_t queue = (dispatch_queue_t)[info objectAtIndex:1]; | |
[__AGEnqueueTable removeObjectForKey:[NSValue valueWithPointer:key]]; | |
[__AGEnqueueRunningSet addObject:[NSValue valueWithPointer:key]]; | |
dispatch_async(queue, ^{ | |
block(); | |
AGEnqueue_doneRunningEnqueuedBlockForKey(key); | |
}); | |
[info release]; | |
} | |
} | |
// Could be called from any thread | |
static void AGEnqueue_doneRunningEnqueuedBlockForKey(void * key) | |
{ | |
dispatch_sync(__AGEnqueueQueue, ^{ | |
[__AGEnqueueRunningSet removeObject:[NSValue valueWithPointer:key]]; | |
AGEnqueue_runEnqueuedBlockForKey(key); | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment