| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- //
- // RACScheduler.m
- // ReactiveObjC
- //
- // Created by Josh Abernathy on 4/16/12.
- // Copyright (c) 2012 GitHub, Inc. All rights reserved.
- //
- #import "RACScheduler.h"
- #import "RACCompoundDisposable.h"
- #import "RACDisposable.h"
- #import "RACImmediateScheduler.h"
- #import "RACScheduler+Private.h"
- #import "RACSubscriptionScheduler.h"
- #import "RACTargetQueueScheduler.h"
- // The key for the thread-specific current scheduler.
- NSString * const RACSchedulerCurrentSchedulerKey = @"RACSchedulerCurrentSchedulerKey";
- @interface RACScheduler ()
- @property (nonatomic, readonly, copy) NSString *name;
- @end
- @implementation RACScheduler
- #pragma mark NSObject
- - (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.name];
- }
- #pragma mark Initializers
- - (instancetype)initWithName:(NSString *)name {
- self = [super init];
- if (name == nil) {
- _name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.%@.anonymousScheduler", self.class];
- } else {
- _name = [name copy];
- }
- return self;
- }
- #pragma mark Schedulers
- + (RACScheduler *)immediateScheduler {
- static dispatch_once_t onceToken;
- static RACScheduler *immediateScheduler;
- dispatch_once(&onceToken, ^{
- immediateScheduler = [[RACImmediateScheduler alloc] init];
- });
-
- return immediateScheduler;
- }
- + (RACScheduler *)mainThreadScheduler {
- static dispatch_once_t onceToken;
- static RACScheduler *mainThreadScheduler;
- dispatch_once(&onceToken, ^{
- mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()];
- });
-
- return mainThreadScheduler;
- }
- + (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
- return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
- }
- + (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority {
- return [self schedulerWithPriority:priority name:@"org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler"];
- }
- + (RACScheduler *)scheduler {
- return [self schedulerWithPriority:RACSchedulerPriorityDefault];
- }
- + (RACScheduler *)subscriptionScheduler {
- static dispatch_once_t onceToken;
- static RACScheduler *subscriptionScheduler;
- dispatch_once(&onceToken, ^{
- subscriptionScheduler = [[RACSubscriptionScheduler alloc] init];
- });
- return subscriptionScheduler;
- }
- + (BOOL)isOnMainThread {
- return [NSOperationQueue.currentQueue isEqual:NSOperationQueue.mainQueue] || [NSThread isMainThread];
- }
- + (RACScheduler *)currentScheduler {
- RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
- if (scheduler != nil) return scheduler;
- if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;
- return nil;
- }
- #pragma mark Scheduling
- - (RACDisposable *)schedule:(void (^)(void))block {
- NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
- return nil;
- }
- - (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
- NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
- return nil;
- }
- - (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block {
- return [self after:[NSDate dateWithTimeIntervalSinceNow:delay] schedule:block];
- }
- - (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
- NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd));
- return nil;
- }
- - (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock {
- RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
- [self scheduleRecursiveBlock:[recursiveBlock copy] addingToDisposable:disposable];
- return disposable;
- }
- - (void)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock addingToDisposable:(RACCompoundDisposable *)disposable {
- @autoreleasepool {
- RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
- [disposable addDisposable:selfDisposable];
- __weak RACDisposable *weakSelfDisposable = selfDisposable;
- RACDisposable *schedulingDisposable = [self schedule:^{
- @autoreleasepool {
- // At this point, we've been invoked, so our disposable is now useless.
- [disposable removeDisposable:weakSelfDisposable];
- }
- if (disposable.disposed) return;
- void (^reallyReschedule)(void) = ^{
- if (disposable.disposed) return;
- [self scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable];
- };
- // Protects the variables below.
- //
- // This doesn't actually need to be __block qualified, but Clang
- // complains otherwise. :C
- __block NSLock *lock = [[NSLock alloc] init];
- lock.name = [NSString stringWithFormat:@"%@ %s", self, sel_getName(_cmd)];
- __block NSUInteger rescheduleCount = 0;
- // Set to YES once synchronous execution has finished. Further
- // rescheduling should occur immediately (rather than being
- // flattened).
- __block BOOL rescheduleImmediately = NO;
- @autoreleasepool {
- recursiveBlock(^{
- [lock lock];
- BOOL immediate = rescheduleImmediately;
- if (!immediate) ++rescheduleCount;
- [lock unlock];
- if (immediate) reallyReschedule();
- });
- }
- [lock lock];
- NSUInteger synchronousCount = rescheduleCount;
- rescheduleImmediately = YES;
- [lock unlock];
- for (NSUInteger i = 0; i < synchronousCount; i++) {
- reallyReschedule();
- }
- }];
- [selfDisposable addDisposable:schedulingDisposable];
- }
- }
- - (void)performAsCurrentScheduler:(void (^)(void))block {
- NSCParameterAssert(block != NULL);
- // If we're using a concurrent queue, we could end up in here concurrently,
- // in which case we *don't* want to clear the current scheduler immediately
- // after our block is done executing, but only *after* all our concurrent
- // invocations are done.
- RACScheduler *previousScheduler = RACScheduler.currentScheduler;
- NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self;
- @autoreleasepool {
- block();
- }
- if (previousScheduler != nil) {
- NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler;
- } else {
- [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey];
- }
- }
- @end
|