├── .gitignore ├── SSignalKit.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SSignalKit ├── Info.plist ├── SAtomic.h ├── SAtomic.m ├── SBag.h ├── SBag.m ├── SBlockDisposable.h ├── SBlockDisposable.m ├── SDisposable.h ├── SDisposableSet.h ├── SDisposableSet.m ├── SMetaDisposable.h ├── SMetaDisposable.m ├── SMulticastSignalManager.h ├── SMulticastSignalManager.m ├── SQueue.h ├── SQueue.m ├── SSignal+Accumulate.h ├── SSignal+Accumulate.m ├── SSignal+Catch.h ├── SSignal+Catch.m ├── SSignal+Combine.h ├── SSignal+Combine.m ├── SSignal+Dispatch.h ├── SSignal+Dispatch.m ├── SSignal+Mapping.h ├── SSignal+Mapping.m ├── SSignal+Meta.h ├── SSignal+Meta.m ├── SSignal+Multicast.h ├── SSignal+Multicast.m ├── SSignal+Pipe.h ├── SSignal+Pipe.m ├── SSignal+SideEffects.h ├── SSignal+SideEffects.m ├── SSignal+Single.h ├── SSignal+Single.m ├── SSignal+Take.h ├── SSignal+Take.m ├── SSignal+Timing.h ├── SSignal+Timing.m ├── SSignal.h ├── SSignal.m ├── SSignalKit.h ├── SSubscriber.h ├── SSubscriber.m ├── SThreadPool.h ├── SThreadPool.m ├── SThreadPoolQueue.h ├── SThreadPoolQueue.m ├── SThreadPoolTask.h ├── SThreadPoolTask.m ├── STimer.h ├── STimer.m ├── SVariable.h └── SVariable.m ├── SSignalKitTests ├── DeallocatingObject.h ├── DeallocatingObject.m ├── Info.plist ├── SDisposableTests.m ├── SSignalBasicTests.m └── SSignalPerformanceTests.m ├── SwiftSignalKit copy-Info.plist ├── SwiftSignalKit ├── Atomic.swift ├── Bag.swift ├── Disposable.swift ├── Info.plist ├── Lock.swift ├── Multicast.swift ├── Promise.swift ├── Queue.swift ├── QueueLocalObject.swift ├── Signal.swift ├── Signal_Catch.swift ├── Signal_Combine.swift ├── Signal_Dispatch.swift ├── Signal_Loop.swift ├── Signal_Mapping.swift ├── Signal_Materialize.swift ├── Signal_Merge.swift ├── Signal_Meta.swift ├── Signal_Reduce.swift ├── Signal_SideEffects.swift ├── Signal_Single.swift ├── Signal_Take.swift ├── Signal_Timing.swift ├── Subscriber.swift ├── SwiftSignalKit.h ├── ThreadPool.swift ├── Timer.swift └── ValuePipe.swift ├── SwiftSignalKitMac ├── Info.plist └── SwiftSignalKitMac.h └── SwiftSignalKitTests ├── DeallocatingObject.swift ├── Info.plist ├── PerformanceTests.swift ├── SwiftSignalKitBasicTests.swift └── SwiftSignalKitFunctionsTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | fastlane/README.md 2 | fastlane/report.xml 3 | fastlane/test_output/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.xcscmblueprint 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | .DS_Store 21 | *.dSYM 22 | *.dSYM.zip 23 | *.ipa 24 | */xcuserdata/* 25 | -------------------------------------------------------------------------------- /SSignalKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SSignalKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SSignalKit/SAtomic.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SAtomic : NSObject 4 | 5 | - (instancetype)initWithValue:(id)value; 6 | - (instancetype)initWithValue:(id)value recursive:(bool)recursive; 7 | - (id)swap:(id)newValue; 8 | - (id)value; 9 | - (id)modify:(id (^)(id))f; 10 | - (id)with:(id (^)(id))f; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /SSignalKit/SAtomic.m: -------------------------------------------------------------------------------- 1 | #import "SAtomic.h" 2 | 3 | #import 4 | 5 | @interface SAtomic () 6 | { 7 | pthread_mutex_t _lock; 8 | pthread_mutexattr_t _attr; 9 | bool _isRecursive; 10 | id _value; 11 | } 12 | 13 | @end 14 | 15 | @implementation SAtomic 16 | 17 | - (instancetype)initWithValue:(id)value 18 | { 19 | self = [super init]; 20 | if (self != nil) 21 | { 22 | pthread_mutex_init(&_lock, NULL); 23 | _value = value; 24 | } 25 | return self; 26 | } 27 | 28 | - (instancetype)initWithValue:(id)value recursive:(bool)recursive { 29 | self = [super init]; 30 | if (self != nil) 31 | { 32 | _isRecursive = recursive; 33 | 34 | if (recursive) { 35 | pthread_mutexattr_init(&_attr); 36 | pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_RECURSIVE); 37 | pthread_mutex_init(&_lock, &_attr); 38 | } else { 39 | pthread_mutex_init(&_lock, NULL); 40 | } 41 | 42 | _value = value; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | if (_isRecursive) { 49 | pthread_mutexattr_destroy(&_attr); 50 | } 51 | pthread_mutex_destroy(&_lock); 52 | } 53 | 54 | - (id)swap:(id)newValue 55 | { 56 | id previousValue = nil; 57 | pthread_mutex_lock(&_lock); 58 | previousValue = _value; 59 | _value = newValue; 60 | pthread_mutex_unlock(&_lock); 61 | return previousValue; 62 | } 63 | 64 | - (id)value 65 | { 66 | id previousValue = nil; 67 | pthread_mutex_lock(&_lock); 68 | previousValue = _value; 69 | pthread_mutex_unlock(&_lock); 70 | 71 | return previousValue; 72 | } 73 | 74 | - (id)modify:(id (^)(id))f 75 | { 76 | id newValue = nil; 77 | pthread_mutex_lock(&_lock); 78 | newValue = f(_value); 79 | _value = newValue; 80 | pthread_mutex_unlock(&_lock); 81 | return newValue; 82 | } 83 | 84 | - (id)with:(id (^)(id))f 85 | { 86 | id result = nil; 87 | pthread_mutex_lock(&_lock); 88 | result = f(_value); 89 | pthread_mutex_unlock(&_lock); 90 | return result; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /SSignalKit/SBag.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SBag : NSObject 4 | 5 | - (NSInteger)addItem:(id)item; 6 | - (void)enumerateItems:(void (^)(id))block; 7 | - (void)removeItem:(NSInteger)key; 8 | - (bool)isEmpty; 9 | - (NSArray *)copyItems; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /SSignalKit/SBag.m: -------------------------------------------------------------------------------- 1 | #import "SBag.h" 2 | 3 | @interface SBag () 4 | { 5 | NSInteger _nextKey; 6 | NSMutableArray *_items; 7 | NSMutableArray *_itemKeys; 8 | } 9 | 10 | @end 11 | 12 | @implementation SBag 13 | 14 | - (instancetype)init 15 | { 16 | self = [super init]; 17 | if (self != nil) 18 | { 19 | _items = [[NSMutableArray alloc] init]; 20 | _itemKeys = [[NSMutableArray alloc] init]; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSInteger)addItem:(id)item 26 | { 27 | if (item == nil) 28 | return -1; 29 | 30 | NSInteger key = _nextKey; 31 | [_items addObject:item]; 32 | [_itemKeys addObject:@(key)]; 33 | _nextKey++; 34 | 35 | return key; 36 | } 37 | 38 | - (void)enumerateItems:(void (^)(id))block 39 | { 40 | if (block) 41 | { 42 | for (id item in _items) 43 | { 44 | block(item); 45 | } 46 | } 47 | } 48 | 49 | - (void)removeItem:(NSInteger)key 50 | { 51 | NSUInteger index = 0; 52 | for (NSNumber *itemKey in _itemKeys) 53 | { 54 | if ([itemKey integerValue] == key) 55 | { 56 | [_items removeObjectAtIndex:index]; 57 | [_itemKeys removeObjectAtIndex:index]; 58 | break; 59 | } 60 | index++; 61 | } 62 | } 63 | 64 | - (bool)isEmpty 65 | { 66 | return _items.count == 0; 67 | } 68 | 69 | - (NSArray *)copyItems 70 | { 71 | return [[NSArray alloc] initWithArray:_items]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /SSignalKit/SBlockDisposable.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SBlockDisposable : NSObject 4 | 5 | - (instancetype)initWithBlock:(void (^)())block; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /SSignalKit/SBlockDisposable.m: -------------------------------------------------------------------------------- 1 | #import "SBlockDisposable.h" 2 | 3 | #import 4 | #import 5 | 6 | @interface SBlockDisposable () 7 | { 8 | void *_block; 9 | } 10 | 11 | @end 12 | 13 | @implementation SBlockDisposable 14 | 15 | - (instancetype)initWithBlock:(void (^)())block 16 | { 17 | self = [super init]; 18 | if (self != nil) 19 | { 20 | _block = (__bridge_retained void *)[block copy]; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)dealloc 26 | { 27 | void *block = _block; 28 | if (block != NULL) 29 | { 30 | if (OSAtomicCompareAndSwapPtr(block, 0, &_block)) 31 | { 32 | if (block != nil) 33 | { 34 | __strong id strongBlock = (__bridge_transfer id)block; 35 | strongBlock = nil; 36 | } 37 | } 38 | } 39 | } 40 | 41 | - (void)dispose 42 | { 43 | void *block = _block; 44 | if (block != NULL) 45 | { 46 | if (OSAtomicCompareAndSwapPtr(block, 0, &_block)) 47 | { 48 | if (block != nil) 49 | { 50 | __strong id strongBlock = (__bridge_transfer id)block; 51 | ((dispatch_block_t)strongBlock)(); 52 | strongBlock = nil; 53 | } 54 | } 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /SSignalKit/SDisposable.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @protocol SDisposable 4 | 5 | - (void)dispose; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /SSignalKit/SDisposableSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SSignal; 4 | 5 | @interface SDisposableSet : NSObject 6 | 7 | - (void)add:(id)disposable; 8 | - (void)remove:(id)disposable; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /SSignalKit/SDisposableSet.m: -------------------------------------------------------------------------------- 1 | #import "SDisposableSet.h" 2 | 3 | #import "SSignal.h" 4 | 5 | #import 6 | 7 | @interface SDisposableSet () 8 | { 9 | OSSpinLock _lock; 10 | bool _disposed; 11 | id _singleDisposable; 12 | NSArray *_multipleDisposables; 13 | } 14 | 15 | @end 16 | 17 | @implementation SDisposableSet 18 | 19 | - (void)add:(id)disposable 20 | { 21 | if (disposable == nil) 22 | return; 23 | 24 | bool dispose = false; 25 | 26 | OSSpinLockLock(&_lock); 27 | dispose = _disposed; 28 | if (!dispose) 29 | { 30 | if (_multipleDisposables != nil) 31 | { 32 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables]; 33 | [multipleDisposables addObject:disposable]; 34 | _multipleDisposables = multipleDisposables; 35 | } 36 | else if (_singleDisposable != nil) 37 | { 38 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithObjects:_singleDisposable, disposable, nil]; 39 | _multipleDisposables = multipleDisposables; 40 | _singleDisposable = nil; 41 | } 42 | else 43 | { 44 | _singleDisposable = disposable; 45 | } 46 | } 47 | OSSpinLockUnlock(&_lock); 48 | 49 | if (dispose) 50 | [disposable dispose]; 51 | } 52 | 53 | - (void)remove:(id)disposable { 54 | OSSpinLockLock(&_lock); 55 | if (_multipleDisposables != nil) 56 | { 57 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables]; 58 | [multipleDisposables removeObject:disposable]; 59 | _multipleDisposables = multipleDisposables; 60 | } 61 | else if (_singleDisposable == disposable) 62 | { 63 | _singleDisposable = nil; 64 | } 65 | OSSpinLockUnlock(&_lock); 66 | } 67 | 68 | - (void)dispose 69 | { 70 | id singleDisposable = nil; 71 | NSArray *multipleDisposables = nil; 72 | 73 | OSSpinLockLock(&_lock); 74 | if (!_disposed) 75 | { 76 | _disposed = true; 77 | singleDisposable = _singleDisposable; 78 | multipleDisposables = _multipleDisposables; 79 | _singleDisposable = nil; 80 | _multipleDisposables = nil; 81 | } 82 | OSSpinLockUnlock(&_lock); 83 | 84 | if (singleDisposable != nil) 85 | [singleDisposable dispose]; 86 | if (multipleDisposables != nil) 87 | { 88 | for (id disposable in multipleDisposables) 89 | { 90 | [disposable dispose]; 91 | } 92 | } 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /SSignalKit/SMetaDisposable.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SMetaDisposable : NSObject 4 | 5 | - (void)setDisposable:(id)disposable; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /SSignalKit/SMetaDisposable.m: -------------------------------------------------------------------------------- 1 | #import "SMetaDisposable.h" 2 | 3 | #import 4 | 5 | @interface SMetaDisposable () 6 | { 7 | OSSpinLock _lock; 8 | bool _disposed; 9 | id _disposable; 10 | } 11 | 12 | @end 13 | 14 | @implementation SMetaDisposable 15 | 16 | - (void)setDisposable:(id)disposable 17 | { 18 | id previousDisposable = nil; 19 | bool dispose = false; 20 | 21 | OSSpinLockLock(&_lock); 22 | dispose = _disposed; 23 | if (!dispose) 24 | { 25 | previousDisposable = _disposable; 26 | _disposable = disposable; 27 | } 28 | OSSpinLockUnlock(&_lock); 29 | 30 | if (previousDisposable != nil) 31 | [previousDisposable dispose]; 32 | 33 | if (dispose) 34 | [disposable dispose]; 35 | } 36 | 37 | - (void)dispose 38 | { 39 | id disposable = nil; 40 | 41 | OSSpinLockLock(&_lock); 42 | if (!_disposed) 43 | { 44 | disposable = _disposable; 45 | _disposed = true; 46 | } 47 | OSSpinLockUnlock(&_lock); 48 | 49 | if (disposable != nil) 50 | [disposable dispose]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /SSignalKit/SMulticastSignalManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SMulticastSignalManager : NSObject 4 | 5 | - (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer; 6 | - (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer; 7 | 8 | - (SSignal *)multicastedPipeForKey:(NSString *)key; 9 | - (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /SSignalKit/SMulticastSignalManager.m: -------------------------------------------------------------------------------- 1 | #import "SMulticastSignalManager.h" 2 | 3 | #import "SSignal+Multicast.h" 4 | #import "SSignal+SideEffects.h" 5 | #import "SBag.h" 6 | #import "SMetaDisposable.h" 7 | #import "SBlockDisposable.h" 8 | 9 | #import 10 | 11 | @interface SMulticastSignalManager () 12 | { 13 | OSSpinLock _lock; 14 | NSMutableDictionary *_multicastSignals; 15 | NSMutableDictionary *_standaloneSignalDisposables; 16 | NSMutableDictionary *_pipeListeners; 17 | } 18 | 19 | @end 20 | 21 | @implementation SMulticastSignalManager 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self != nil) 27 | { 28 | _multicastSignals = [[NSMutableDictionary alloc] init]; 29 | _standaloneSignalDisposables = [[NSMutableDictionary alloc] init]; 30 | _pipeListeners = [[NSMutableDictionary alloc] init]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)dealloc 36 | { 37 | NSArray *disposables = nil; 38 | OSSpinLockLock(&_lock); 39 | disposables = [_standaloneSignalDisposables allValues]; 40 | OSSpinLockUnlock(&_lock); 41 | 42 | for (id disposable in disposables) 43 | { 44 | [disposable dispose]; 45 | } 46 | } 47 | 48 | - (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer 49 | { 50 | if (key == nil) 51 | { 52 | if (producer) 53 | return producer(); 54 | else 55 | return nil; 56 | } 57 | 58 | SSignal *signal = nil; 59 | OSSpinLockLock(&_lock); 60 | signal = _multicastSignals[key]; 61 | if (signal == nil) 62 | { 63 | __weak SMulticastSignalManager *weakSelf = self; 64 | if (producer) 65 | signal = producer(); 66 | if (signal != nil) 67 | { 68 | signal = [[signal onDispose:^ 69 | { 70 | __strong SMulticastSignalManager *strongSelf = weakSelf; 71 | if (strongSelf != nil) 72 | { 73 | OSSpinLockLock(&strongSelf->_lock); 74 | [strongSelf->_multicastSignals removeObjectForKey:key]; 75 | OSSpinLockUnlock(&strongSelf->_lock); 76 | } 77 | }] multicast]; 78 | _multicastSignals[key] = signal; 79 | } 80 | } 81 | OSSpinLockUnlock(&_lock); 82 | 83 | return signal; 84 | } 85 | 86 | - (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer 87 | { 88 | if (key == nil) 89 | return; 90 | 91 | bool produce = false; 92 | OSSpinLockLock(&_lock); 93 | if (_standaloneSignalDisposables[key] == nil) 94 | { 95 | _standaloneSignalDisposables[key] = [[SMetaDisposable alloc] init]; 96 | produce = true; 97 | } 98 | OSSpinLockUnlock(&_lock); 99 | 100 | if (produce) 101 | { 102 | __weak SMulticastSignalManager *weakSelf = self; 103 | id disposable = [producer() startWithNext:nil error:^(__unused id error) 104 | { 105 | __strong SMulticastSignalManager *strongSelf = weakSelf; 106 | if (strongSelf != nil) 107 | { 108 | OSSpinLockLock(&strongSelf->_lock); 109 | [strongSelf->_standaloneSignalDisposables removeObjectForKey:key]; 110 | OSSpinLockUnlock(&strongSelf->_lock); 111 | } 112 | } completed:^ 113 | { 114 | __strong SMulticastSignalManager *strongSelf = weakSelf; 115 | if (strongSelf != nil) 116 | { 117 | OSSpinLockLock(&strongSelf->_lock); 118 | [strongSelf->_standaloneSignalDisposables removeObjectForKey:key]; 119 | OSSpinLockUnlock(&strongSelf->_lock); 120 | } 121 | }]; 122 | 123 | OSSpinLockLock(&_lock); 124 | [(SMetaDisposable *)_standaloneSignalDisposables[key] setDisposable:disposable]; 125 | OSSpinLockUnlock(&_lock); 126 | } 127 | } 128 | 129 | - (SSignal *)multicastedPipeForKey:(NSString *)key 130 | { 131 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 132 | { 133 | OSSpinLockLock(&_lock); 134 | SBag *bag = _pipeListeners[key]; 135 | if (bag == nil) 136 | { 137 | bag = [[SBag alloc] init]; 138 | _pipeListeners[key] = bag; 139 | } 140 | NSInteger index = [bag addItem:[^(id next) 141 | { 142 | [subscriber putNext:next]; 143 | } copy]]; 144 | OSSpinLockUnlock(&_lock); 145 | 146 | return [[SBlockDisposable alloc] initWithBlock:^ 147 | { 148 | OSSpinLockLock(&_lock); 149 | SBag *bag = _pipeListeners[key]; 150 | [bag removeItem:index]; 151 | if ([bag isEmpty]) { 152 | [_pipeListeners removeObjectForKey:key]; 153 | } 154 | OSSpinLockUnlock(&_lock); 155 | }]; 156 | }]; 157 | } 158 | 159 | - (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key 160 | { 161 | OSSpinLockLock(&_lock); 162 | NSArray *pipeListeners = [(SBag *)_pipeListeners[key] copyItems]; 163 | OSSpinLockUnlock(&_lock); 164 | 165 | for (void (^listener)(id) in pipeListeners) 166 | { 167 | listener(next); 168 | } 169 | } 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /SSignalKit/SQueue.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SQueue : NSObject 4 | 5 | + (SQueue *)mainQueue; 6 | + (SQueue *)concurrentDefaultQueue; 7 | + (SQueue *)concurrentBackgroundQueue; 8 | 9 | + (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue; 10 | 11 | - (void)dispatch:(dispatch_block_t)block; 12 | - (void)dispatchSync:(dispatch_block_t)block; 13 | - (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous; 14 | 15 | - (dispatch_queue_t)_dispatch_queue; 16 | 17 | - (bool)isCurrentQueue; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SSignalKit/SQueue.m: -------------------------------------------------------------------------------- 1 | #import "SQueue.h" 2 | 3 | static const void *SQueueSpecificKey = &SQueueSpecificKey; 4 | 5 | @interface SQueue () 6 | { 7 | dispatch_queue_t _queue; 8 | void *_queueSpecific; 9 | bool _specialIsMainQueue; 10 | } 11 | 12 | @end 13 | 14 | @implementation SQueue 15 | 16 | + (SQueue *)mainQueue 17 | { 18 | static SQueue *queue = nil; 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^ 21 | { 22 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_main_queue() queueSpecific:NULL]; 23 | queue->_specialIsMainQueue = true; 24 | }); 25 | 26 | return queue; 27 | } 28 | 29 | + (SQueue *)concurrentDefaultQueue 30 | { 31 | static SQueue *queue = nil; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^ 34 | { 35 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) queueSpecific:NULL]; 36 | }); 37 | 38 | return queue; 39 | } 40 | 41 | + (SQueue *)concurrentBackgroundQueue 42 | { 43 | static SQueue *queue = nil; 44 | static dispatch_once_t onceToken; 45 | dispatch_once(&onceToken, ^ 46 | { 47 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) queueSpecific:NULL]; 48 | }); 49 | 50 | return queue; 51 | } 52 | 53 | + (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue 54 | { 55 | return [[SQueue alloc] initWithNativeQueue:nativeQueue queueSpecific:NULL]; 56 | } 57 | 58 | - (instancetype)init 59 | { 60 | dispatch_queue_t queue = dispatch_queue_create(NULL, NULL); 61 | dispatch_queue_set_specific(queue, SQueueSpecificKey, (__bridge void *)self, NULL); 62 | return [self initWithNativeQueue:queue queueSpecific:(__bridge void *)self]; 63 | } 64 | 65 | - (instancetype)initWithNativeQueue:(dispatch_queue_t)queue queueSpecific:(void *)queueSpecific 66 | { 67 | self = [super init]; 68 | if (self != nil) 69 | { 70 | _queue = queue; 71 | _queueSpecific = queueSpecific; 72 | } 73 | return self; 74 | } 75 | 76 | - (dispatch_queue_t)_dispatch_queue 77 | { 78 | return _queue; 79 | } 80 | 81 | - (void)dispatch:(dispatch_block_t)block 82 | { 83 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific) 84 | block(); 85 | else if (_specialIsMainQueue && [NSThread isMainThread]) 86 | block(); 87 | else 88 | dispatch_async(_queue, block); 89 | } 90 | 91 | - (void)dispatchSync:(dispatch_block_t)block 92 | { 93 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific) 94 | block(); 95 | else if (_specialIsMainQueue && [NSThread isMainThread]) 96 | block(); 97 | else 98 | dispatch_sync(_queue, block); 99 | } 100 | 101 | - (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous { 102 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific) 103 | block(); 104 | else if (_specialIsMainQueue && [NSThread isMainThread]) 105 | block(); 106 | else { 107 | if (synchronous) { 108 | dispatch_sync(_queue, block); 109 | } else { 110 | dispatch_async(_queue, block); 111 | } 112 | } 113 | } 114 | 115 | - (bool)isCurrentQueue 116 | { 117 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific) 118 | return true; 119 | else if (_specialIsMainQueue && [NSThread isMainThread]) 120 | return true; 121 | return false; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Accumulate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Accumulate) 4 | 5 | - (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f; 6 | - (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Accumulate.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Accumulate.h" 2 | 3 | @implementation SSignal (Accumulate) 4 | 5 | - (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f 6 | { 7 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber) 8 | { 9 | __block id intermediateResult = value; 10 | 11 | return [self startWithNext:^(id next) 12 | { 13 | intermediateResult = f(intermediateResult, next); 14 | } error:^(id error) 15 | { 16 | [subscriber putError:error]; 17 | } completed:^ 18 | { 19 | if (intermediateResult != nil) 20 | [subscriber putNext:intermediateResult]; 21 | [subscriber putCompletion]; 22 | }]; 23 | }]; 24 | } 25 | 26 | - (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f 27 | { 28 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber) 29 | { 30 | __block id intermediateResult = value; 31 | 32 | void (^emit)(id) = ^(id next) 33 | { 34 | [subscriber putNext:next]; 35 | }; 36 | 37 | return [self startWithNext:^(id next) 38 | { 39 | intermediateResult = f(intermediateResult, next, emit); 40 | } error:^(id error) 41 | { 42 | [subscriber putError:error]; 43 | } completed:^ 44 | { 45 | if (intermediateResult != nil) 46 | [subscriber putNext:intermediateResult]; 47 | [subscriber putCompletion]; 48 | }]; 49 | }]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Catch.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Catch) 4 | 5 | - (SSignal *)catch:(SSignal *(^)(id error))f; 6 | - (SSignal *)restart; 7 | - (SSignal *)retryIf:(bool (^)(id error))predicate; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Catch.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Catch.h" 2 | 3 | #import "SMetaDisposable.h" 4 | #import "SDisposableSet.h" 5 | #import "SBlockDisposable.h" 6 | #import "SAtomic.h" 7 | 8 | @implementation SSignal (Catch) 9 | 10 | - (SSignal *)catch:(SSignal *(^)(id error))f 11 | { 12 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 13 | { 14 | SDisposableSet *disposable = [[SDisposableSet alloc] init]; 15 | 16 | [disposable add:[self startWithNext:^(id next) 17 | { 18 | [subscriber putNext:next]; 19 | } error:^(id error) 20 | { 21 | SSignal *signal = f(error); 22 | [disposable add:[signal startWithNext:^(id next) 23 | { 24 | [subscriber putNext:next]; 25 | } error:^(id error) 26 | { 27 | [subscriber putError:error]; 28 | } completed:^ 29 | { 30 | [subscriber putCompletion]; 31 | }]]; 32 | } completed:^ 33 | { 34 | [subscriber putCompletion]; 35 | }]]; 36 | 37 | return disposable; 38 | }]; 39 | } 40 | 41 | static dispatch_block_t recursiveBlock(void (^block)(dispatch_block_t recurse)) 42 | { 43 | return ^ 44 | { 45 | block(recursiveBlock(block)); 46 | }; 47 | } 48 | 49 | - (SSignal *)restart 50 | { 51 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 52 | { 53 | SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true]; 54 | 55 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init]; 56 | 57 | void (^start)() = recursiveBlock(^(dispatch_block_t recurse) 58 | { 59 | NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current) 60 | { 61 | return current; 62 | }]; 63 | 64 | if ([currentShouldRestart boolValue]) 65 | { 66 | id disposable = [self startWithNext:^(id next) 67 | { 68 | [subscriber putNext:next]; 69 | } error:^(id error) 70 | { 71 | [subscriber putError:error]; 72 | } completed:^ 73 | { 74 | recurse(); 75 | }]; 76 | [currentDisposable setDisposable:disposable]; 77 | } 78 | }); 79 | 80 | start(); 81 | 82 | return [[SBlockDisposable alloc] initWithBlock:^ 83 | { 84 | [currentDisposable dispose]; 85 | 86 | [shouldRestart modify:^id(__unused id current) 87 | { 88 | return @false; 89 | }]; 90 | }]; 91 | }]; 92 | } 93 | 94 | - (SSignal *)retryIf:(bool (^)(id error))predicate { 95 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 96 | { 97 | SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true]; 98 | 99 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init]; 100 | 101 | void (^start)() = recursiveBlock(^(dispatch_block_t recurse) 102 | { 103 | NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current) 104 | { 105 | return current; 106 | }]; 107 | 108 | if ([currentShouldRestart boolValue]) 109 | { 110 | id disposable = [self startWithNext:^(id next) 111 | { 112 | [subscriber putNext:next]; 113 | } error:^(id error) 114 | { 115 | if (predicate(error)) { 116 | recurse(); 117 | } else { 118 | [subscriber putError:error]; 119 | } 120 | } completed:^ 121 | { 122 | [shouldRestart modify:^id(__unused id current) { 123 | return @false; 124 | }]; 125 | [subscriber putCompletion]; 126 | }]; 127 | [currentDisposable setDisposable:disposable]; 128 | } else { 129 | [subscriber putCompletion]; 130 | } 131 | }); 132 | 133 | start(); 134 | 135 | return [[SBlockDisposable alloc] initWithBlock:^ 136 | { 137 | [currentDisposable dispose]; 138 | 139 | [shouldRestart modify:^id(__unused id current) 140 | { 141 | return @false; 142 | }]; 143 | }]; 144 | }]; 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Combine.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Combine) 4 | 5 | + (SSignal *)combineSignals:(NSArray *)signals; 6 | + (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates; 7 | 8 | + (SSignal *)mergeSignals:(NSArray *)signals; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Combine.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Combine.h" 2 | 3 | #import "SAtomic.h" 4 | #import "SDisposableSet.h" 5 | #import "SSignal+Single.h" 6 | 7 | @interface SSignalCombineState : NSObject 8 | 9 | @property (nonatomic, strong, readonly) NSDictionary *latestValues; 10 | @property (nonatomic, strong, readonly) NSArray *completedStatuses; 11 | @property (nonatomic) bool error; 12 | 13 | @end 14 | 15 | @implementation SSignalCombineState 16 | 17 | - (instancetype)initWithLatestValues:(NSDictionary *)latestValues completedStatuses:(NSArray *)completedStatuses error:(bool)error 18 | { 19 | self = [super init]; 20 | if (self != nil) 21 | { 22 | _latestValues = latestValues; 23 | _completedStatuses = completedStatuses; 24 | _error = error; 25 | } 26 | return self; 27 | } 28 | 29 | @end 30 | 31 | @implementation SSignal (Combine) 32 | 33 | + (SSignal *)combineSignals:(NSArray *)signals 34 | { 35 | if (signals.count == 0) 36 | return [SSignal single:@[]]; 37 | else 38 | return [self combineSignals:signals withInitialStates:nil]; 39 | } 40 | 41 | + (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates 42 | { 43 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber) 44 | { 45 | NSMutableArray *completedStatuses = [[NSMutableArray alloc] init]; 46 | for (NSUInteger i = 0; i < signals.count; i++) 47 | { 48 | [completedStatuses addObject:@false]; 49 | } 50 | NSMutableDictionary *initialLatestValues = [[NSMutableDictionary alloc] init]; 51 | for (NSUInteger i = 0; i < initialStates.count; i++) 52 | { 53 | initialLatestValues[@(i)] = initialStates[i]; 54 | } 55 | SAtomic *combineState = [[SAtomic alloc] initWithValue:[[SSignalCombineState alloc] initWithLatestValues:initialLatestValues completedStatuses:completedStatuses error:false]]; 56 | 57 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init]; 58 | 59 | NSUInteger index = 0; 60 | NSUInteger count = signals.count; 61 | for (SSignal *signal in signals) 62 | { 63 | id disposable = [signal startWithNext:^(id next) 64 | { 65 | SSignalCombineState *currentState = [combineState modify:^id(SSignalCombineState *state) 66 | { 67 | NSMutableDictionary *latestValues = [[NSMutableDictionary alloc] initWithDictionary:state.latestValues]; 68 | latestValues[@(index)] = next; 69 | return [[SSignalCombineState alloc] initWithLatestValues:latestValues completedStatuses:state.completedStatuses error:state.error]; 70 | }]; 71 | NSMutableArray *latestValues = [[NSMutableArray alloc] init]; 72 | for (NSUInteger i = 0; i < count; i++) 73 | { 74 | id value = currentState.latestValues[@(i)]; 75 | if (value == nil) 76 | { 77 | latestValues = nil; 78 | break; 79 | } 80 | latestValues[i] = value; 81 | } 82 | if (latestValues != nil) 83 | [subscriber putNext:latestValues]; 84 | } 85 | error:^(id error) 86 | { 87 | __block bool hadError = false; 88 | [combineState modify:^id(SSignalCombineState *state) 89 | { 90 | hadError = state.error; 91 | return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:state.completedStatuses error:true]; 92 | }]; 93 | if (!hadError) 94 | [subscriber putError:error]; 95 | } completed:^ 96 | { 97 | __block bool wasCompleted = false; 98 | __block bool isCompleted = false; 99 | [combineState modify:^id(SSignalCombineState *state) 100 | { 101 | NSMutableArray *completedStatuses = [[NSMutableArray alloc] initWithArray:state.completedStatuses]; 102 | bool everyStatusWasCompleted = true; 103 | for (NSNumber *nStatus in completedStatuses) 104 | { 105 | if (![nStatus boolValue]) 106 | { 107 | everyStatusWasCompleted = false; 108 | break; 109 | } 110 | } 111 | completedStatuses[index] = @true; 112 | bool everyStatusIsCompleted = true; 113 | for (NSNumber *nStatus in completedStatuses) 114 | { 115 | if (![nStatus boolValue]) 116 | { 117 | everyStatusIsCompleted = false; 118 | break; 119 | } 120 | } 121 | 122 | wasCompleted = everyStatusWasCompleted; 123 | isCompleted = everyStatusIsCompleted; 124 | 125 | return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:completedStatuses error:state.error]; 126 | }]; 127 | if (!wasCompleted && isCompleted) 128 | [subscriber putCompletion]; 129 | }]; 130 | [compositeDisposable add:disposable]; 131 | index++; 132 | } 133 | 134 | return compositeDisposable; 135 | }]; 136 | } 137 | 138 | + (SSignal *)mergeSignals:(NSArray *)signals 139 | { 140 | if (signals.count == 0) 141 | return [SSignal complete]; 142 | 143 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 144 | { 145 | SDisposableSet *disposables = [[SDisposableSet alloc] init]; 146 | SAtomic *completedStates = [[SAtomic alloc] initWithValue:[[NSSet alloc] init]]; 147 | 148 | NSInteger index = -1; 149 | NSUInteger count = signals.count; 150 | for (SSignal *signal in signals) 151 | { 152 | index++; 153 | 154 | id disposable = [signal startWithNext:^(id next) 155 | { 156 | [subscriber putNext:next]; 157 | } error:^(id error) 158 | { 159 | [subscriber putError:error]; 160 | } completed:^ 161 | { 162 | NSSet *set = [completedStates modify:^id(NSSet *set) 163 | { 164 | return [set setByAddingObject:@(index)]; 165 | }]; 166 | if (set.count == count) 167 | [subscriber putCompletion]; 168 | }]; 169 | 170 | [disposables add:disposable]; 171 | } 172 | 173 | return disposables; 174 | }]; 175 | } 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Dispatch.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | #import 5 | 6 | @interface SSignal (Dispatch) 7 | 8 | - (SSignal *)deliverOn:(SQueue *)queue; 9 | - (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool; 10 | - (SSignal *)startOn:(SQueue *)queue; 11 | - (SSignal *)startOnThreadPool:(SThreadPool *)threadPool; 12 | - (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Dispatch.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Dispatch.h" 2 | #import "SAtomic.h" 3 | #import "STimer.h" 4 | #import "SBlockDisposable.h" 5 | #import "SMetaDisposable.h" 6 | 7 | @interface SSignal_ThrottleContainer : NSObject 8 | 9 | @property (nonatomic, strong, readonly) id value; 10 | @property (nonatomic, readonly) bool committed; 11 | @property (nonatomic, readonly) bool last; 12 | 13 | @end 14 | 15 | @implementation SSignal_ThrottleContainer 16 | 17 | - (instancetype)initWithValue:(id)value committed:(bool)committed last:(bool)last { 18 | self = [super init]; 19 | if (self != nil) { 20 | _value = value; 21 | _committed = committed; 22 | _last = last; 23 | } 24 | return self; 25 | } 26 | 27 | @end 28 | 29 | @implementation SSignal (Dispatch) 30 | 31 | - (SSignal *)deliverOn:(SQueue *)queue 32 | { 33 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 34 | { 35 | return [self startWithNext:^(id next) 36 | { 37 | [queue dispatch:^ 38 | { 39 | [subscriber putNext:next]; 40 | }]; 41 | } error:^(id error) 42 | { 43 | [queue dispatch:^ 44 | { 45 | [subscriber putError:error]; 46 | }]; 47 | } completed:^ 48 | { 49 | [queue dispatch:^ 50 | { 51 | [subscriber putCompletion]; 52 | }]; 53 | }]; 54 | }]; 55 | } 56 | 57 | - (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool 58 | { 59 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 60 | { 61 | SThreadPoolQueue *queue = [threadPool nextQueue]; 62 | return [self startWithNext:^(id next) 63 | { 64 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)()) 65 | { 66 | if (!cancelled()) 67 | [subscriber putNext:next]; 68 | }]; 69 | [queue addTask:task]; 70 | } error:^(id error) 71 | { 72 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)()) 73 | { 74 | if (!cancelled()) 75 | [subscriber putError:error]; 76 | }]; 77 | [queue addTask:task]; 78 | } completed:^ 79 | { 80 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)()) 81 | { 82 | if (!cancelled()) 83 | [subscriber putCompletion]; 84 | }]; 85 | [queue addTask:task]; 86 | }]; 87 | }]; 88 | } 89 | 90 | - (SSignal *)startOn:(SQueue *)queue 91 | { 92 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 93 | { 94 | __block bool isCancelled = false; 95 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init]; 96 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^ 97 | { 98 | isCancelled = true; 99 | }]]; 100 | 101 | [queue dispatch:^ 102 | { 103 | if (!isCancelled) 104 | { 105 | [disposable setDisposable:[self startWithNext:^(id next) 106 | { 107 | [subscriber putNext:next]; 108 | } error:^(id error) 109 | { 110 | [subscriber putError:error]; 111 | } completed:^ 112 | { 113 | [subscriber putCompletion]; 114 | }]]; 115 | } 116 | }]; 117 | 118 | return disposable; 119 | }]; 120 | } 121 | 122 | - (SSignal *)startOnThreadPool:(SThreadPool *)threadPool 123 | { 124 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 125 | { 126 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init]; 127 | 128 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)()) 129 | { 130 | if (cancelled && cancelled()) 131 | return; 132 | 133 | [disposable setDisposable:[self startWithNext:^(id next) 134 | { 135 | [subscriber putNext:next]; 136 | } error:^(id error) 137 | { 138 | [subscriber putError:error]; 139 | } completed:^ 140 | { 141 | [subscriber putCompletion]; 142 | }]]; 143 | }]; 144 | 145 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^ 146 | { 147 | [task cancel]; 148 | }]]; 149 | 150 | [threadPool addTask:task]; 151 | 152 | return disposable; 153 | }]; 154 | } 155 | 156 | - (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay 157 | { 158 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { 159 | SAtomic *value = [[SAtomic alloc] initWithValue:nil]; 160 | STimer *timer = [[STimer alloc] initWithTimeout:delay repeat:false completion:^{ 161 | [value modify:^id(SSignal_ThrottleContainer *container) { 162 | if (container != nil) { 163 | if (!container.committed) { 164 | [subscriber putNext:container.value]; 165 | container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:true last:container.last]; 166 | } 167 | 168 | if (container.last) { 169 | [subscriber putCompletion]; 170 | } 171 | } 172 | return container; 173 | }]; 174 | } queue:queue]; 175 | 176 | return [[self deliverOn:queue] startWithNext:^(id next) { 177 | [value modify:^id(SSignal_ThrottleContainer *container) { 178 | if (container == nil) { 179 | container = [[SSignal_ThrottleContainer alloc] initWithValue:next committed:false last:false]; 180 | } 181 | return container; 182 | }]; 183 | [timer invalidate]; 184 | [timer start]; 185 | } error:^(id error) { 186 | [timer invalidate]; 187 | [subscriber putError:error]; 188 | } completed:^{ 189 | [timer invalidate]; 190 | __block bool start = false; 191 | [value modify:^id(SSignal_ThrottleContainer *container) { 192 | bool wasCommitted = false; 193 | if (container == nil) { 194 | wasCommitted = true; 195 | container = [[SSignal_ThrottleContainer alloc] initWithValue:nil committed:true last:true]; 196 | } else { 197 | wasCommitted = container.committed; 198 | container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:container.committed last:true]; 199 | } 200 | start = wasCommitted; 201 | return container; 202 | }]; 203 | if (start) { 204 | [timer start]; 205 | } else { 206 | [timer fireAndInvalidate]; 207 | } 208 | }]; 209 | }]; 210 | } 211 | 212 | @end 213 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Mapping.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Mapping) 4 | 5 | - (SSignal *)map:(id (^)(id))f; 6 | - (SSignal *)filter:(bool (^)(id))f; 7 | - (SSignal *)ignoreRepeated; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Mapping.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Mapping.h" 2 | 3 | #import "SAtomic.h" 4 | 5 | @interface SSignalIgnoreRepeatedState: NSObject 6 | 7 | @property (nonatomic, strong) id value; 8 | @property (nonatomic) bool hasValue; 9 | 10 | @end 11 | 12 | @implementation SSignalIgnoreRepeatedState 13 | 14 | @end 15 | 16 | @implementation SSignal (Mapping) 17 | 18 | - (SSignal *)map:(id (^)(id))f 19 | { 20 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 21 | { 22 | return [self startWithNext:^(id next) 23 | { 24 | [subscriber putNext:f(next)]; 25 | } error:^(id error) 26 | { 27 | [subscriber putError:error]; 28 | } completed:^ 29 | { 30 | [subscriber putCompletion]; 31 | }]; 32 | }]; 33 | } 34 | 35 | - (SSignal *)filter:(bool (^)(id))f 36 | { 37 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 38 | { 39 | return [self startWithNext:^(id next) 40 | { 41 | if (f(next)) 42 | [subscriber putNext:next]; 43 | } error:^(id error) 44 | { 45 | [subscriber putError:error]; 46 | } completed:^ 47 | { 48 | [subscriber putCompletion]; 49 | }]; 50 | }]; 51 | } 52 | 53 | - (SSignal *)ignoreRepeated { 54 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { 55 | SAtomic *state = [[SAtomic alloc] initWithValue:[[SSignalIgnoreRepeatedState alloc] init]]; 56 | 57 | return [self startWithNext:^(id next) { 58 | bool shouldPassthrough = [[state with:^id(SSignalIgnoreRepeatedState *state) { 59 | if (!state.hasValue) { 60 | state.hasValue = true; 61 | state.value = next; 62 | return @true; 63 | } else if ((state.value == nil && next == nil) || [(id)state.value isEqual:next]) { 64 | return @false; 65 | } 66 | state.value = next; 67 | return @true; 68 | }] boolValue]; 69 | 70 | if (shouldPassthrough) { 71 | [subscriber putNext:next]; 72 | } 73 | } error:^(id error) 74 | { 75 | [subscriber putError:error]; 76 | } completed:^ 77 | { 78 | [subscriber putCompletion]; 79 | }]; 80 | }]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Meta.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SQueue; 4 | 5 | @interface SSignal (Meta) 6 | 7 | - (SSignal *)switchToLatest; 8 | - (SSignal *)mapToSignal:(SSignal *(^)(id))f; 9 | - (SSignal *)mapToQueue:(SSignal *(^)(id))f; 10 | - (SSignal *)mapToThrottled:(SSignal *(^)(id))f; 11 | - (SSignal *)then:(SSignal *)signal; 12 | - (SSignal *)queue; 13 | - (SSignal *)throttled; 14 | + (SSignal *)defer:(SSignal *(^)())generator; 15 | 16 | @end 17 | 18 | @interface SSignalQueue : NSObject 19 | 20 | - (SSignal *)enqueue:(SSignal *)signal; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Meta.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Meta.h" 2 | 3 | #import "SDisposableSet.h" 4 | #import "SMetaDisposable.h" 5 | #import "SSignal+Mapping.h" 6 | #import "SAtomic.h" 7 | #import "SSignal+Pipe.h" 8 | 9 | #import 10 | 11 | @interface SSignalQueueState : NSObject 12 | { 13 | OSSpinLock _lock; 14 | bool _executingSignal; 15 | bool _terminated; 16 | 17 | id _disposable; 18 | SMetaDisposable *_currentDisposable; 19 | SSubscriber *_subscriber; 20 | 21 | NSMutableArray *_queuedSignals; 22 | bool _queueMode; 23 | bool _throttleMode; 24 | } 25 | 26 | @end 27 | 28 | @implementation SSignalQueueState 29 | 30 | - (instancetype)initWithSubscriber:(SSubscriber *)subscriber queueMode:(bool)queueMode throttleMode:(bool)throttleMode 31 | { 32 | self = [super init]; 33 | if (self != nil) 34 | { 35 | _subscriber = subscriber; 36 | _currentDisposable = [[SMetaDisposable alloc] init]; 37 | _queuedSignals = queueMode ? [[NSMutableArray alloc] init] : nil; 38 | _queueMode = queueMode; 39 | _throttleMode = throttleMode; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)beginWithDisposable:(id)disposable 45 | { 46 | _disposable = disposable; 47 | } 48 | 49 | - (void)enqueueSignal:(SSignal *)signal 50 | { 51 | bool startSignal = false; 52 | OSSpinLockLock(&_lock); 53 | if (_queueMode && _executingSignal) { 54 | if (_throttleMode) { 55 | [_queuedSignals removeAllObjects]; 56 | } 57 | [_queuedSignals addObject:signal]; 58 | } 59 | else 60 | { 61 | _executingSignal = true; 62 | startSignal = true; 63 | } 64 | OSSpinLockUnlock(&_lock); 65 | 66 | if (startSignal) 67 | { 68 | __weak SSignalQueueState *weakSelf = self; 69 | id disposable = [signal startWithNext:^(id next) 70 | { 71 | [_subscriber putNext:next]; 72 | } error:^(id error) 73 | { 74 | [_subscriber putError:error]; 75 | } completed:^ 76 | { 77 | __strong SSignalQueueState *strongSelf = weakSelf; 78 | if (strongSelf != nil) { 79 | [strongSelf headCompleted]; 80 | } 81 | }]; 82 | 83 | [_currentDisposable setDisposable:disposable]; 84 | } 85 | } 86 | 87 | - (void)headCompleted 88 | { 89 | SSignal *nextSignal = nil; 90 | 91 | bool terminated = false; 92 | OSSpinLockLock(&_lock); 93 | _executingSignal = false; 94 | 95 | if (_queueMode) 96 | { 97 | if (_queuedSignals.count != 0) 98 | { 99 | nextSignal = _queuedSignals[0]; 100 | [_queuedSignals removeObjectAtIndex:0]; 101 | _executingSignal = true; 102 | } 103 | else 104 | terminated = _terminated; 105 | } 106 | else 107 | terminated = _terminated; 108 | OSSpinLockUnlock(&_lock); 109 | 110 | if (terminated) 111 | [_subscriber putCompletion]; 112 | else if (nextSignal != nil) 113 | { 114 | __weak SSignalQueueState *weakSelf = self; 115 | id disposable = [nextSignal startWithNext:^(id next) 116 | { 117 | [_subscriber putNext:next]; 118 | } error:^(id error) 119 | { 120 | [_subscriber putError:error]; 121 | } completed:^ 122 | { 123 | __strong SSignalQueueState *strongSelf = weakSelf; 124 | if (strongSelf != nil) { 125 | [strongSelf headCompleted]; 126 | } 127 | }]; 128 | 129 | [_currentDisposable setDisposable:disposable]; 130 | } 131 | } 132 | 133 | - (void)beginCompletion 134 | { 135 | bool executingSignal = false; 136 | OSSpinLockLock(&_lock); 137 | executingSignal = _executingSignal; 138 | _terminated = true; 139 | OSSpinLockUnlock(&_lock); 140 | 141 | if (!executingSignal) 142 | [_subscriber putCompletion]; 143 | } 144 | 145 | - (void)dispose 146 | { 147 | [_currentDisposable dispose]; 148 | [_disposable dispose]; 149 | } 150 | 151 | @end 152 | 153 | @implementation SSignal (Meta) 154 | 155 | - (SSignal *)switchToLatest 156 | { 157 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 158 | { 159 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:false throttleMode:false]; 160 | 161 | [state beginWithDisposable:[self startWithNext:^(id next) 162 | { 163 | [state enqueueSignal:next]; 164 | } error:^(id error) 165 | { 166 | [subscriber putError:error]; 167 | } completed:^ 168 | { 169 | [state beginCompletion]; 170 | }]]; 171 | 172 | return state; 173 | }]; 174 | } 175 | 176 | - (SSignal *)mapToSignal:(SSignal *(^)(id))f 177 | { 178 | return [[self map:f] switchToLatest]; 179 | } 180 | 181 | - (SSignal *)mapToQueue:(SSignal *(^)(id))f 182 | { 183 | return [[self map:f] queue]; 184 | } 185 | 186 | - (SSignal *)mapToThrottled:(SSignal *(^)(id))f { 187 | return [[self map:f] throttled]; 188 | } 189 | 190 | - (SSignal *)then:(SSignal *)signal 191 | { 192 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber) 193 | { 194 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init]; 195 | 196 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init]; 197 | [compositeDisposable add:currentDisposable]; 198 | 199 | [currentDisposable setDisposable:[self startWithNext:^(id next) 200 | { 201 | [subscriber putNext:next]; 202 | } error:^(id error) 203 | { 204 | [subscriber putError:error]; 205 | } completed:^ 206 | { 207 | [compositeDisposable add:[signal startWithNext:^(id next) 208 | { 209 | [subscriber putNext:next]; 210 | } error:^(id error) 211 | { 212 | [subscriber putError:error]; 213 | } completed:^ 214 | { 215 | [subscriber putCompletion]; 216 | }]]; 217 | }]]; 218 | 219 | return compositeDisposable; 220 | }]; 221 | } 222 | 223 | - (SSignal *)queue 224 | { 225 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 226 | { 227 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:false]; 228 | 229 | [state beginWithDisposable:[self startWithNext:^(id next) 230 | { 231 | [state enqueueSignal:next]; 232 | } error:^(id error) 233 | { 234 | [subscriber putError:error]; 235 | } completed:^ 236 | { 237 | [state beginCompletion]; 238 | }]]; 239 | 240 | return state; 241 | }]; 242 | } 243 | 244 | - (SSignal *)throttled { 245 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { 246 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:true]; 247 | [state beginWithDisposable:[self startWithNext:^(id next) 248 | { 249 | [state enqueueSignal:next]; 250 | } error:^(id error) 251 | { 252 | [subscriber putError:error]; 253 | } completed:^ 254 | { 255 | [state beginCompletion]; 256 | }]]; 257 | 258 | return state; 259 | }]; 260 | } 261 | 262 | + (SSignal *)defer:(SSignal *(^)())generator 263 | { 264 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 265 | { 266 | return [generator() startWithNext:^(id next) 267 | { 268 | [subscriber putNext:next]; 269 | } error:^(id error) 270 | { 271 | [subscriber putError:error]; 272 | } completed:^ 273 | { 274 | [subscriber putCompletion]; 275 | }]; 276 | }]; 277 | } 278 | 279 | @end 280 | 281 | @interface SSignalQueue () { 282 | SPipe *_pipe; 283 | id _disposable; 284 | } 285 | 286 | @end 287 | 288 | @implementation SSignalQueue 289 | 290 | - (instancetype)init { 291 | self = [super init]; 292 | if (self != nil) { 293 | _pipe = [[SPipe alloc] init]; 294 | _disposable = [[_pipe.signalProducer() queue] startWithNext:nil]; 295 | } 296 | return self; 297 | } 298 | 299 | - (void)dealloc { 300 | [_disposable dispose]; 301 | } 302 | 303 | - (SSignal *)enqueue:(SSignal *)signal { 304 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { 305 | SPipe *disposePipe = [[SPipe alloc] init]; 306 | 307 | SSignal *proxy = [[[[signal onNext:^(id next) { 308 | [subscriber putNext:next]; 309 | }] onError:^(id error) { 310 | [subscriber putError:error]; 311 | }] onCompletion:^{ 312 | [subscriber putCompletion]; 313 | }] catch:^SSignal *(__unused id error) { 314 | return [SSignal complete]; 315 | }]; 316 | 317 | _pipe.sink([proxy takeUntilReplacement:disposePipe.signalProducer()]); 318 | 319 | return [[SBlockDisposable alloc] initWithBlock:^{ 320 | disposePipe.sink([SSignal complete]); 321 | }]; 322 | }]; 323 | } 324 | 325 | @end 326 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Multicast.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Multicast) 4 | 5 | - (SSignal *)multicast; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Multicast.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Multicast.h" 2 | 3 | #import 4 | #import "SBag.h" 5 | #import "SBlockDisposable.h" 6 | 7 | typedef enum { 8 | SSignalMulticastStateReady, 9 | SSignalMulticastStateStarted, 10 | SSignalMulticastStateCompleted 11 | } SSignalMulticastState; 12 | 13 | @interface SSignalMulticastSubscribers : NSObject 14 | { 15 | volatile OSSpinLock _lock; 16 | SBag *_subscribers; 17 | SSignalMulticastState _state; 18 | id _disposable; 19 | } 20 | 21 | @end 22 | 23 | @implementation SSignalMulticastSubscribers 24 | 25 | - (instancetype)init 26 | { 27 | self = [super init]; 28 | if (self != nil) 29 | { 30 | _subscribers = [[SBag alloc] init]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)setDisposable:(id)disposable 36 | { 37 | [_disposable dispose]; 38 | _disposable = disposable; 39 | } 40 | 41 | - (id)addSubscriber:(SSubscriber *)subscriber start:(bool *)start 42 | { 43 | OSSpinLockLock(&_lock); 44 | NSInteger index = [_subscribers addItem:subscriber]; 45 | switch (_state) { 46 | case SSignalMulticastStateReady: 47 | *start = true; 48 | _state = SSignalMulticastStateStarted; 49 | break; 50 | default: 51 | break; 52 | } 53 | OSSpinLockUnlock(&_lock); 54 | 55 | return [[SBlockDisposable alloc] initWithBlock:^ 56 | { 57 | [self remove:index]; 58 | }]; 59 | } 60 | 61 | - (void)remove:(NSInteger)index 62 | { 63 | id currentDisposable = nil; 64 | 65 | OSSpinLockLock(&_lock); 66 | [_subscribers removeItem:index]; 67 | switch (_state) { 68 | case SSignalMulticastStateStarted: 69 | if ([_subscribers isEmpty]) 70 | { 71 | currentDisposable = _disposable; 72 | _disposable = nil; 73 | } 74 | break; 75 | default: 76 | break; 77 | } 78 | OSSpinLockUnlock(&_lock); 79 | 80 | [currentDisposable dispose]; 81 | } 82 | 83 | - (void)notifyNext:(id)next 84 | { 85 | NSArray *currentSubscribers = nil; 86 | OSSpinLockLock(&_lock); 87 | currentSubscribers = [_subscribers copyItems]; 88 | OSSpinLockUnlock(&_lock); 89 | 90 | for (SSubscriber *subscriber in currentSubscribers) 91 | { 92 | [subscriber putNext:next]; 93 | } 94 | } 95 | 96 | - (void)notifyError:(id)error 97 | { 98 | NSArray *currentSubscribers = nil; 99 | OSSpinLockLock(&_lock); 100 | currentSubscribers = [_subscribers copyItems]; 101 | _state = SSignalMulticastStateCompleted; 102 | OSSpinLockUnlock(&_lock); 103 | 104 | for (SSubscriber *subscriber in currentSubscribers) 105 | { 106 | [subscriber putError:error]; 107 | } 108 | } 109 | 110 | - (void)notifyCompleted 111 | { 112 | NSArray *currentSubscribers = nil; 113 | OSSpinLockLock(&_lock); 114 | currentSubscribers = [_subscribers copyItems]; 115 | _state = SSignalMulticastStateCompleted; 116 | OSSpinLockUnlock(&_lock); 117 | 118 | for (SSubscriber *subscriber in currentSubscribers) 119 | { 120 | [subscriber putCompletion]; 121 | } 122 | } 123 | 124 | @end 125 | 126 | @implementation SSignal (Multicast) 127 | 128 | - (SSignal *)multicast 129 | { 130 | SSignalMulticastSubscribers *subscribers = [[SSignalMulticastSubscribers alloc] init]; 131 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 132 | { 133 | bool start = false; 134 | id currentDisposable = [subscribers addSubscriber:subscriber start:&start]; 135 | if (start) 136 | { 137 | id disposable = [self startWithNext:^(id next) 138 | { 139 | [subscribers notifyNext:next]; 140 | } error:^(id error) 141 | { 142 | [subscribers notifyError:error]; 143 | } completed:^ 144 | { 145 | [subscribers notifyCompleted]; 146 | }]; 147 | 148 | [subscribers setDisposable:[[SBlockDisposable alloc] initWithBlock:^ 149 | { 150 | [disposable dispose]; 151 | }]]; 152 | } 153 | 154 | return currentDisposable; 155 | }]; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Pipe.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SPipe : NSObject 4 | 5 | @property (nonatomic, copy, readonly) SSignal *(^signalProducer)(); 6 | @property (nonatomic, copy, readonly) void (^sink)(id); 7 | 8 | - (instancetype)initWithReplay:(bool)replay; 9 | 10 | @end 11 | 12 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Pipe.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Pipe.h" 2 | 3 | #import "SBlockDisposable.h" 4 | #import "SAtomic.h" 5 | #import "SBag.h" 6 | 7 | @interface SPipeReplayState : NSObject 8 | 9 | @property (nonatomic, readonly) bool hasReceivedValue; 10 | @property (nonatomic, strong, readonly) id recentValue; 11 | 12 | @end 13 | 14 | @implementation SPipeReplayState 15 | 16 | - (instancetype)initWithReceivedValue:(bool)receivedValue recentValue:(id)recentValue 17 | { 18 | self = [super init]; 19 | if (self != nil) 20 | { 21 | _hasReceivedValue = receivedValue; 22 | _recentValue = recentValue; 23 | } 24 | return self; 25 | } 26 | 27 | @end 28 | 29 | @implementation SPipe 30 | 31 | - (instancetype)init 32 | { 33 | return [self initWithReplay:false]; 34 | } 35 | 36 | - (instancetype)initWithReplay:(bool)replay 37 | { 38 | self = [super init]; 39 | if (self != nil) 40 | { 41 | SAtomic *subscribers = [[SAtomic alloc] initWithValue:[[SBag alloc] init]]; 42 | SAtomic *replayState = replay ? [[SAtomic alloc] initWithValue:[[SPipeReplayState alloc] initWithReceivedValue:false recentValue:nil]] : nil; 43 | 44 | _signalProducer = [^SSignal * 45 | { 46 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 47 | { 48 | __block NSUInteger index = 0; 49 | [subscribers with:^id(SBag *bag) 50 | { 51 | index = [bag addItem:[^(id next) 52 | { 53 | [subscriber putNext:next]; 54 | } copy]]; 55 | return nil; 56 | }]; 57 | 58 | if (replay) 59 | { 60 | [replayState with:^id(SPipeReplayState *state) 61 | { 62 | if (state.hasReceivedValue) 63 | [subscriber putNext:state.recentValue]; 64 | return nil; 65 | }]; 66 | } 67 | 68 | return [[SBlockDisposable alloc] initWithBlock:^ 69 | { 70 | [subscribers with:^id(SBag *bag) 71 | { 72 | [bag removeItem:index]; 73 | return nil; 74 | }]; 75 | }]; 76 | }]; 77 | } copy]; 78 | 79 | _sink = [^(id next) 80 | { 81 | NSArray *items = [subscribers with:^id(SBag *bag) 82 | { 83 | return [bag copyItems]; 84 | }]; 85 | 86 | for (void (^item)(id) in items) 87 | { 88 | item(next); 89 | } 90 | 91 | if (replay) 92 | { 93 | [replayState modify:^id(__unused SPipeReplayState *state) 94 | { 95 | return [[SPipeReplayState alloc] initWithReceivedValue:true recentValue:next]; 96 | }]; 97 | } 98 | } copy]; 99 | } 100 | return self; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+SideEffects.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (SideEffects) 4 | 5 | - (SSignal *)onStart:(void (^)())f; 6 | - (SSignal *)onNext:(void (^)(id next))f; 7 | - (SSignal *)afterNext:(void (^)(id next))f; 8 | - (SSignal *)onError:(void (^)(id error))f; 9 | - (SSignal *)onCompletion:(void (^)())f; 10 | - (SSignal *)afterCompletion:(void (^)())f; 11 | - (SSignal *)onDispose:(void (^)())f; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+SideEffects.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+SideEffects.h" 2 | 3 | #import "SBlockDisposable.h" 4 | #import "SDisposableSet.h" 5 | 6 | @implementation SSignal (SideEffects) 7 | 8 | - (SSignal *)onStart:(void (^)())f 9 | { 10 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 11 | { 12 | f(); 13 | return [self startWithNext:^(id next) 14 | { 15 | [subscriber putNext:next]; 16 | } error:^(id error) 17 | { 18 | [subscriber putError:error]; 19 | } completed:^ 20 | { 21 | [subscriber putCompletion]; 22 | }]; 23 | }]; 24 | } 25 | 26 | - (SSignal *)onNext:(void (^)(id next))f 27 | { 28 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 29 | { 30 | return [self startWithNext:^(id next) 31 | { 32 | f(next); 33 | [subscriber putNext:next]; 34 | } error:^(id error) 35 | { 36 | [subscriber putError:error]; 37 | } completed:^ 38 | { 39 | [subscriber putCompletion]; 40 | }]; 41 | }]; 42 | } 43 | 44 | - (SSignal *)afterNext:(void (^)(id next))f 45 | { 46 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 47 | { 48 | return [self startWithNext:^(id next) 49 | { 50 | [subscriber putNext:next]; 51 | f(next); 52 | } error:^(id error) 53 | { 54 | [subscriber putError:error]; 55 | } completed:^ 56 | { 57 | [subscriber putCompletion]; 58 | }]; 59 | }]; 60 | } 61 | 62 | - (SSignal *)onError:(void (^)(id error))f 63 | { 64 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 65 | { 66 | return [self startWithNext:^(id next) 67 | { 68 | [subscriber putNext:next]; 69 | } error:^(id error) 70 | { 71 | f(error); 72 | [subscriber putError:error]; 73 | } completed:^ 74 | { 75 | [subscriber putCompletion]; 76 | }]; 77 | }]; 78 | } 79 | 80 | - (SSignal *)onCompletion:(void (^)())f 81 | { 82 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 83 | { 84 | return [self startWithNext:^(id next) 85 | { 86 | [subscriber putNext:next]; 87 | } error:^(id error) 88 | { 89 | [subscriber putError:error]; 90 | } completed:^ 91 | { 92 | f(); 93 | [subscriber putCompletion]; 94 | }]; 95 | }]; 96 | } 97 | 98 | - (SSignal *)afterCompletion:(void (^)())f { 99 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 100 | { 101 | return [self startWithNext:^(id next) 102 | { 103 | [subscriber putNext:next]; 104 | } error:^(id error) 105 | { 106 | [subscriber putError:error]; 107 | } completed:^ 108 | { 109 | [subscriber putCompletion]; 110 | f(); 111 | }]; 112 | }]; 113 | } 114 | 115 | - (SSignal *)onDispose:(void (^)())f 116 | { 117 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber) 118 | { 119 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init]; 120 | 121 | [compositeDisposable add:[self startWithNext:^(id next) 122 | { 123 | [subscriber putNext:next]; 124 | } error:^(id error) 125 | { 126 | [subscriber putError:error]; 127 | } completed:^ 128 | { 129 | [subscriber putCompletion]; 130 | }]]; 131 | 132 | [compositeDisposable add:[[SBlockDisposable alloc] initWithBlock:^ 133 | { 134 | f(); 135 | }]]; 136 | 137 | return compositeDisposable; 138 | }]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Single.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Single) 4 | 5 | + (SSignal *)single:(id)next; 6 | + (SSignal *)fail:(id)error; 7 | + (SSignal *)never; 8 | + (SSignal *)complete; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Single.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Single.h" 2 | 3 | @implementation SSignal (Single) 4 | 5 | + (SSignal *)single:(id)next 6 | { 7 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 8 | { 9 | [subscriber putNext:next]; 10 | [subscriber putCompletion]; 11 | return nil; 12 | }]; 13 | } 14 | 15 | + (SSignal *)fail:(id)error 16 | { 17 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 18 | { 19 | [subscriber putError:error]; 20 | return nil; 21 | }]; 22 | } 23 | 24 | + (SSignal *)never 25 | { 26 | return [[SSignal alloc] initWithGenerator:^id (__unused SSubscriber *subscriber) 27 | { 28 | return nil; 29 | }]; 30 | } 31 | 32 | + (SSignal *)complete 33 | { 34 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 35 | { 36 | [subscriber putCompletion]; 37 | return nil; 38 | }]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Take.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal (Take) 4 | 5 | - (SSignal *)take:(NSUInteger)count; 6 | - (SSignal *)takeLast; 7 | - (SSignal *)takeUntilReplacement:(SSignal *)replacement; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Take.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Take.h" 2 | 3 | #import "SAtomic.h" 4 | 5 | @interface SSignal_ValueContainer : NSObject 6 | 7 | @property (nonatomic, strong, readonly) id value; 8 | 9 | @end 10 | 11 | @implementation SSignal_ValueContainer 12 | 13 | - (instancetype)initWithValue:(id)value { 14 | self = [super init]; 15 | if (self != nil) { 16 | _value = value; 17 | } 18 | return self; 19 | } 20 | 21 | @end 22 | 23 | @implementation SSignal (Take) 24 | 25 | - (SSignal *)take:(NSUInteger)count 26 | { 27 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 28 | { 29 | SAtomic *counter = [[SAtomic alloc] initWithValue:@(0)]; 30 | return [self startWithNext:^(id next) 31 | { 32 | __block bool passthrough = false; 33 | __block bool complete = false; 34 | [counter modify:^id(NSNumber *currentCount) 35 | { 36 | NSUInteger updatedCount = [currentCount unsignedIntegerValue] + 1; 37 | if (updatedCount <= count) 38 | passthrough = true; 39 | if (updatedCount == count) 40 | complete = true; 41 | return @(updatedCount); 42 | }]; 43 | 44 | if (passthrough) 45 | [subscriber putNext:next]; 46 | if (complete) 47 | [subscriber putCompletion]; 48 | } error:^(id error) 49 | { 50 | [subscriber putError:error]; 51 | } completed:^ 52 | { 53 | [subscriber putCompletion]; 54 | }]; 55 | }]; 56 | } 57 | 58 | - (SSignal *)takeLast 59 | { 60 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 61 | { 62 | SAtomic *last = [[SAtomic alloc] initWithValue:nil]; 63 | return [self startWithNext:^(id next) 64 | { 65 | [last swap:[[SSignal_ValueContainer alloc] initWithValue:next]]; 66 | } error:^(id error) 67 | { 68 | [subscriber putError:error]; 69 | } completed:^ 70 | { 71 | SSignal_ValueContainer *value = [last with:^id(id value) { 72 | return value; 73 | }]; 74 | if (value != nil) 75 | { 76 | [subscriber putNext:value.value]; 77 | } 78 | [subscriber putCompletion]; 79 | }]; 80 | }]; 81 | } 82 | 83 | - (SSignal *)takeUntilReplacement:(SSignal *)replacement { 84 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { 85 | SDisposableSet *disposable = [[SDisposableSet alloc] init]; 86 | 87 | SMetaDisposable *selfDisposable = [[SMetaDisposable alloc] init]; 88 | SMetaDisposable *replacementDisposable = [[SMetaDisposable alloc] init]; 89 | 90 | [disposable add:selfDisposable]; 91 | [disposable add:replacementDisposable]; 92 | 93 | [disposable add:[replacement startWithNext:^(SSignal *next) { 94 | [selfDisposable dispose]; 95 | 96 | [replacementDisposable setDisposable:[next startWithNext:^(id next) { 97 | [subscriber putNext:next]; 98 | } error:^(id error) { 99 | [subscriber putError:error]; 100 | } completed:^{ 101 | [subscriber putCompletion]; 102 | }]]; 103 | } error:^(id error) { 104 | [subscriber putError:error]; 105 | } completed:^{ 106 | }]]; 107 | 108 | [selfDisposable setDisposable:[self startWithNext:^(id next) { 109 | [subscriber putNext:next]; 110 | } error:^(id error) { 111 | [replacementDisposable dispose]; 112 | [subscriber putError:error]; 113 | } completed:^{ 114 | [replacementDisposable dispose]; 115 | [subscriber putCompletion]; 116 | }]]; 117 | 118 | return disposable; 119 | }]; 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Timing.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | 5 | @interface SSignal (Timing) 6 | 7 | - (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue; 8 | - (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal; 9 | - (SSignal *)wait:(NSTimeInterval)seconds; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /SSignalKit/SSignal+Timing.m: -------------------------------------------------------------------------------- 1 | #import "SSignal+Timing.h" 2 | 3 | #import "SMetaDisposable.h" 4 | #import "SDisposableSet.h" 5 | #import "SBlockDisposable.h" 6 | 7 | #import "SSignal+Dispatch.h" 8 | 9 | #import "STimer.h" 10 | 11 | @implementation SSignal (Timing) 12 | 13 | - (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue 14 | { 15 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 16 | { 17 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init]; 18 | 19 | STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^ 20 | { 21 | [disposable setDisposable:[self startWithNext:^(id next) 22 | { 23 | [subscriber putNext:next]; 24 | } error:^(id error) 25 | { 26 | [subscriber putError:error]; 27 | } completed:^ 28 | { 29 | [subscriber putCompletion]; 30 | }]]; 31 | } queue:queue]; 32 | 33 | [timer start]; 34 | 35 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^ 36 | { 37 | [timer invalidate]; 38 | }]]; 39 | 40 | return disposable; 41 | }]; 42 | } 43 | 44 | - (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal 45 | { 46 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber) 47 | { 48 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init]; 49 | 50 | STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^ 51 | { 52 | [disposable setDisposable:[signal startWithNext:^(id next) 53 | { 54 | [subscriber putNext:next]; 55 | } error:^(id error) 56 | { 57 | [subscriber putError:error]; 58 | } completed:^ 59 | { 60 | [subscriber putCompletion]; 61 | }]]; 62 | } queue:queue]; 63 | [timer start]; 64 | 65 | [disposable setDisposable:[self startWithNext:^(id next) 66 | { 67 | [timer invalidate]; 68 | [subscriber putNext:next]; 69 | } error:^(id error) 70 | { 71 | [timer invalidate]; 72 | [subscriber putError:error]; 73 | } completed:^ 74 | { 75 | [timer invalidate]; 76 | [subscriber putCompletion]; 77 | }]]; 78 | 79 | return disposable; 80 | }]; 81 | } 82 | 83 | - (SSignal *)wait:(NSTimeInterval)seconds 84 | { 85 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 86 | { 87 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 88 | 89 | id disposable = [self startWithNext:^(id next) 90 | { 91 | dispatch_semaphore_signal(semaphore); 92 | [subscriber putNext:next]; 93 | } error:^(id error) 94 | { 95 | dispatch_semaphore_signal(semaphore); 96 | [subscriber putError:error]; 97 | } completed:^ 98 | { 99 | dispatch_semaphore_signal(semaphore); 100 | [subscriber putCompletion]; 101 | }]; 102 | 103 | dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC))); 104 | 105 | return disposable; 106 | }]; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /SSignalKit/SSignal.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSignal : NSObject 4 | { 5 | @public 6 | id (^_generator)(SSubscriber *); 7 | } 8 | 9 | - (instancetype)initWithGenerator:(id (^)(SSubscriber *))generator; 10 | 11 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed; 12 | - (id)startWithNext:(void (^)(id next))next; 13 | - (id)startWithNext:(void (^)(id next))next completed:(void (^)())completed; 14 | 15 | - (SSignal *)trace:(NSString *)name; 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /SSignalKit/SSignal.m: -------------------------------------------------------------------------------- 1 | #import "SSignal.h" 2 | 3 | #import "SBlockDisposable.h" 4 | 5 | @interface SSubscriberDisposable : NSObject 6 | { 7 | SSubscriber *_subscriber; 8 | id _disposable; 9 | } 10 | 11 | @end 12 | 13 | @implementation SSubscriberDisposable 14 | 15 | - (instancetype)initWithSubscriber:(SSubscriber *)subscriber disposable:(id)disposable 16 | { 17 | self = [super init]; 18 | if (self != nil) 19 | { 20 | _subscriber = subscriber; 21 | _disposable = disposable; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)dispose 27 | { 28 | [_subscriber _markTerminatedWithoutDisposal]; 29 | [_disposable dispose]; 30 | } 31 | 32 | @end 33 | 34 | @interface SSignal () 35 | { 36 | } 37 | 38 | @end 39 | 40 | @implementation SSignal 41 | 42 | - (instancetype)initWithGenerator:(id (^)(SSubscriber *))generator 43 | { 44 | self = [super init]; 45 | if (self != nil) 46 | { 47 | _generator = [generator copy]; 48 | } 49 | return self; 50 | } 51 | 52 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed traceName:(NSString *)traceName 53 | { 54 | STracingSubscriber *subscriber = [[STracingSubscriber alloc] initWithName:traceName next:next error:error completed:completed]; 55 | id disposable = _generator(subscriber); 56 | [subscriber _assignDisposable:disposable]; 57 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable]; 58 | } 59 | 60 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed 61 | { 62 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:error completed:completed]; 63 | id disposable = _generator(subscriber); 64 | [subscriber _assignDisposable:disposable]; 65 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable]; 66 | } 67 | 68 | - (id)startWithNext:(void (^)(id next))next 69 | { 70 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:nil]; 71 | id disposable = _generator(subscriber); 72 | [subscriber _assignDisposable:disposable]; 73 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable]; 74 | } 75 | 76 | - (id)startWithNext:(void (^)(id next))next completed:(void (^)())completed 77 | { 78 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:completed]; 79 | id disposable = _generator(subscriber); 80 | [subscriber _assignDisposable:disposable]; 81 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable]; 82 | } 83 | 84 | - (SSignal *)trace:(NSString *)name 85 | { 86 | #ifdef DEBUG 87 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 88 | { 89 | NSString *traceName = [[NSString alloc] initWithFormat:@"%@#0x%x", name, (int)random()]; 90 | NSLog(@"trace(%@ start)", traceName); 91 | return [self startWithNext:^(id next) 92 | { 93 | [subscriber putNext:next]; 94 | } error:^(id error) 95 | { 96 | [subscriber putError:error]; 97 | } completed:^ 98 | { 99 | [subscriber putCompletion]; 100 | } traceName:traceName]; 101 | }]; 102 | #else 103 | return self; 104 | #endif 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /SSignalKit/SSignalKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSignalKit.h 3 | // SSignalKit 4 | // 5 | // Created by Peter on 31/01/15. 6 | // Copyright (c) 2015 Telegram. All rights reserved. 7 | // 8 | 9 | #if __IPHONE_OS_VERSION_MIN_REQUIRED 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | //! Project version number for SSignalKit. 16 | FOUNDATION_EXPORT double SSignalKitVersionNumber; 17 | 18 | //! Project version string for SSignalKit. 19 | FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[]; 20 | 21 | // In this header, you should import all the public headers of your framework using statements like #import 22 | 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #import 39 | #import 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | #import 46 | -------------------------------------------------------------------------------- /SSignalKit/SSubscriber.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SSubscriber : NSObject 4 | { 5 | } 6 | 7 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed; 8 | 9 | - (void)_assignDisposable:(id)disposable; 10 | - (void)_markTerminatedWithoutDisposal; 11 | 12 | - (void)putNext:(id)next; 13 | - (void)putError:(id)error; 14 | - (void)putCompletion; 15 | 16 | @end 17 | 18 | @interface STracingSubscriber : SSubscriber 19 | 20 | - (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed; 21 | 22 | @end -------------------------------------------------------------------------------- /SSignalKit/SSubscriber.m: -------------------------------------------------------------------------------- 1 | #import "SSubscriber.h" 2 | 3 | #import 4 | 5 | @interface SSubscriberBlocks : NSObject { 6 | @public 7 | void (^_next)(id); 8 | void (^_error)(id); 9 | void (^_completed)(); 10 | } 11 | 12 | @end 13 | 14 | @implementation SSubscriberBlocks 15 | 16 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed { 17 | self = [super init]; 18 | if (self != nil) { 19 | _next = [next copy]; 20 | _error = [error copy]; 21 | _completed = [completed copy]; 22 | } 23 | return self; 24 | } 25 | 26 | @end 27 | 28 | @interface SSubscriber () 29 | { 30 | @protected 31 | OSSpinLock _lock; 32 | bool _terminated; 33 | id _disposable; 34 | SSubscriberBlocks *_blocks; 35 | } 36 | 37 | @end 38 | 39 | @implementation SSubscriber 40 | 41 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed 42 | { 43 | self = [super init]; 44 | if (self != nil) 45 | { 46 | _blocks = [[SSubscriberBlocks alloc] initWithNext:next error:error completed:completed]; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)_assignDisposable:(id)disposable 52 | { 53 | bool dispose = false; 54 | OSSpinLockLock(&_lock); 55 | if (_terminated) { 56 | dispose = true; 57 | } else { 58 | _disposable = disposable; 59 | } 60 | OSSpinLockUnlock(&_lock); 61 | if (dispose) { 62 | [disposable dispose]; 63 | } 64 | } 65 | 66 | - (void)_markTerminatedWithoutDisposal 67 | { 68 | OSSpinLockLock(&_lock); 69 | SSubscriberBlocks *blocks = nil; 70 | if (!_terminated) 71 | { 72 | blocks = _blocks; 73 | _blocks = nil; 74 | 75 | _terminated = true; 76 | } 77 | OSSpinLockUnlock(&_lock); 78 | 79 | if (blocks) { 80 | blocks = nil; 81 | } 82 | } 83 | 84 | - (void)putNext:(id)next 85 | { 86 | SSubscriberBlocks *blocks = nil; 87 | 88 | OSSpinLockLock(&_lock); 89 | if (!_terminated) { 90 | blocks = _blocks; 91 | } 92 | OSSpinLockUnlock(&_lock); 93 | 94 | if (blocks && blocks->_next) { 95 | blocks->_next(next); 96 | } 97 | } 98 | 99 | - (void)putError:(id)error 100 | { 101 | bool shouldDispose = false; 102 | SSubscriberBlocks *blocks = nil; 103 | 104 | OSSpinLockLock(&_lock); 105 | if (!_terminated) 106 | { 107 | blocks = _blocks; 108 | _blocks = nil; 109 | 110 | shouldDispose = true; 111 | _terminated = true; 112 | } 113 | OSSpinLockUnlock(&_lock); 114 | 115 | if (blocks && blocks->_error) { 116 | blocks->_error(error); 117 | } 118 | 119 | if (shouldDispose) 120 | [self->_disposable dispose]; 121 | } 122 | 123 | - (void)putCompletion 124 | { 125 | bool shouldDispose = false; 126 | SSubscriberBlocks *blocks = nil; 127 | 128 | OSSpinLockLock(&_lock); 129 | if (!_terminated) 130 | { 131 | blocks = _blocks; 132 | _blocks = nil; 133 | 134 | shouldDispose = true; 135 | _terminated = true; 136 | } 137 | OSSpinLockUnlock(&_lock); 138 | 139 | if (blocks && blocks->_completed) 140 | blocks->_completed(); 141 | 142 | if (shouldDispose) 143 | [self->_disposable dispose]; 144 | } 145 | 146 | - (void)dispose 147 | { 148 | [self->_disposable dispose]; 149 | } 150 | 151 | @end 152 | 153 | @interface STracingSubscriber () 154 | { 155 | NSString *_name; 156 | } 157 | 158 | @end 159 | 160 | @implementation STracingSubscriber 161 | 162 | - (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed 163 | { 164 | self = [super initWithNext:next error:error completed:completed]; 165 | if (self != nil) 166 | { 167 | _name = name; 168 | } 169 | return self; 170 | } 171 | 172 | /*- (void)_assignDisposable:(id)disposable 173 | { 174 | if (_terminated) 175 | [disposable dispose]; 176 | else 177 | _disposable = disposable; 178 | } 179 | 180 | - (void)_markTerminatedWithoutDisposal 181 | { 182 | OSSpinLockLock(&_lock); 183 | if (!_terminated) 184 | { 185 | NSLog(@"trace(%@ terminated)", _name); 186 | _terminated = true; 187 | _next = nil; 188 | _error = nil; 189 | _completed = nil; 190 | } 191 | OSSpinLockUnlock(&_lock); 192 | } 193 | 194 | - (void)putNext:(id)next 195 | { 196 | void (^fnext)(id) = nil; 197 | 198 | OSSpinLockLock(&_lock); 199 | if (!_terminated) 200 | fnext = self->_next; 201 | OSSpinLockUnlock(&_lock); 202 | 203 | if (fnext) 204 | { 205 | NSLog(@"trace(%@ next: %@)", _name, next); 206 | fnext(next); 207 | } 208 | else 209 | NSLog(@"trace(%@ next: %@, not accepted)", _name, next); 210 | } 211 | 212 | - (void)putError:(id)error 213 | { 214 | bool shouldDispose = false; 215 | void (^ferror)(id) = nil; 216 | 217 | OSSpinLockLock(&_lock); 218 | if (!_terminated) 219 | { 220 | ferror = self->_error; 221 | shouldDispose = true; 222 | self->_next = nil; 223 | self->_error = nil; 224 | self->_completed = nil; 225 | _terminated = true; 226 | } 227 | OSSpinLockUnlock(&_lock); 228 | 229 | if (ferror) 230 | { 231 | NSLog(@"trace(%@ error: %@)", _name, error); 232 | ferror(error); 233 | } 234 | else 235 | NSLog(@"trace(%@ error: %@, not accepted)", _name, error); 236 | 237 | if (shouldDispose) 238 | [self->_disposable dispose]; 239 | } 240 | 241 | - (void)putCompletion 242 | { 243 | bool shouldDispose = false; 244 | void (^completed)() = nil; 245 | 246 | OSSpinLockLock(&_lock); 247 | if (!_terminated) 248 | { 249 | completed = self->_completed; 250 | shouldDispose = true; 251 | self->_next = nil; 252 | self->_error = nil; 253 | self->_completed = nil; 254 | _terminated = true; 255 | } 256 | OSSpinLockUnlock(&_lock); 257 | 258 | if (completed) 259 | { 260 | NSLog(@"trace(%@ completed)", _name); 261 | completed(); 262 | } 263 | else 264 | NSLog(@"trace(%@ completed, not accepted)", _name); 265 | 266 | if (shouldDispose) 267 | [self->_disposable dispose]; 268 | } 269 | 270 | - (void)dispose 271 | { 272 | NSLog(@"trace(%@ dispose)", _name); 273 | [self->_disposable dispose]; 274 | }*/ 275 | 276 | @end 277 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPool.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | #import 5 | 6 | @interface SThreadPool : NSObject 7 | 8 | - (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority; 9 | 10 | - (void)addTask:(SThreadPoolTask *)task; 11 | 12 | - (SThreadPoolQueue *)nextQueue; 13 | - (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPool.m: -------------------------------------------------------------------------------- 1 | #import "SThreadPool.h" 2 | 3 | #import 4 | #import 5 | #import "SQueue.h" 6 | 7 | @interface SThreadPool () 8 | { 9 | SQueue *_managementQueue; 10 | NSMutableArray *_threads; 11 | 12 | NSMutableArray *_queues; 13 | NSMutableArray *_takenQueues; 14 | 15 | pthread_mutex_t _mutex; 16 | pthread_cond_t _cond; 17 | } 18 | 19 | @end 20 | 21 | @implementation SThreadPool 22 | 23 | + (void)threadEntryPoint:(SThreadPool *)threadPool 24 | { 25 | SThreadPoolQueue *queue = nil; 26 | 27 | while (true) 28 | { 29 | SThreadPoolTask *task = nil; 30 | 31 | pthread_mutex_lock(&threadPool->_mutex); 32 | 33 | if (queue != nil) 34 | { 35 | [threadPool->_takenQueues removeObject:queue]; 36 | if ([queue _hasTasks]) 37 | [threadPool->_queues addObject:queue]; 38 | } 39 | 40 | while (true) 41 | { 42 | while (threadPool->_queues.count == 0) 43 | pthread_cond_wait(&threadPool->_cond, &threadPool->_mutex); 44 | 45 | queue = threadPool->_queues.firstObject; 46 | task = [queue _popFirstTask]; 47 | 48 | if (queue != nil) 49 | { 50 | [threadPool->_takenQueues addObject:queue]; 51 | [threadPool->_queues removeObjectAtIndex:0]; 52 | 53 | break; 54 | } 55 | } 56 | pthread_mutex_unlock(&threadPool->_mutex); 57 | 58 | @autoreleasepool 59 | { 60 | [task execute]; 61 | } 62 | } 63 | } 64 | 65 | - (instancetype)init 66 | { 67 | return [self initWithThreadCount:2 threadPriority:0.5]; 68 | } 69 | 70 | - (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority 71 | { 72 | self = [super init]; 73 | if (self != nil) 74 | { 75 | pthread_mutex_init(&_mutex, 0); 76 | pthread_cond_init(&_cond, 0); 77 | 78 | _managementQueue = [[SQueue alloc] init]; 79 | 80 | [_managementQueue dispatch:^ 81 | { 82 | _threads = [[NSMutableArray alloc] init]; 83 | _queues = [[NSMutableArray alloc] init]; 84 | _takenQueues = [[NSMutableArray alloc] init]; 85 | for (NSUInteger i = 0; i < threadCount; i++) 86 | { 87 | NSThread *thread = [[NSThread alloc] initWithTarget:[SThreadPool class] selector:@selector(threadEntryPoint:) object:self]; 88 | thread.name = [[NSString alloc] initWithFormat:@"SThreadPool-%p-%d", self, (int)i]; 89 | [thread setThreadPriority:threadPriority]; 90 | [_threads addObject:thread]; 91 | [thread start]; 92 | } 93 | }]; 94 | } 95 | return self; 96 | } 97 | 98 | - (void)dealloc 99 | { 100 | pthread_mutex_destroy(&_mutex); 101 | pthread_cond_destroy(&_cond); 102 | } 103 | 104 | - (void)addTask:(SThreadPoolTask *)task 105 | { 106 | SThreadPoolQueue *tempQueue = [self nextQueue]; 107 | [tempQueue addTask:task]; 108 | } 109 | 110 | - (SThreadPoolQueue *)nextQueue 111 | { 112 | return [[SThreadPoolQueue alloc] initWithThreadPool:self]; 113 | } 114 | 115 | - (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block 116 | { 117 | [_managementQueue dispatch:^ 118 | { 119 | pthread_mutex_lock(&_mutex); 120 | block(); 121 | if (![_queues containsObject:queue] && ![_takenQueues containsObject:queue]) 122 | [_queues addObject:queue]; 123 | pthread_cond_broadcast(&_cond); 124 | pthread_mutex_unlock(&_mutex); 125 | }]; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPoolQueue.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SThreadPool; 4 | @class SThreadPoolTask; 5 | 6 | @interface SThreadPoolQueue : NSObject 7 | 8 | - (instancetype)initWithThreadPool:(SThreadPool *)threadPool; 9 | - (void)addTask:(SThreadPoolTask *)task; 10 | - (SThreadPoolTask *)_popFirstTask; 11 | - (bool)_hasTasks; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPoolQueue.m: -------------------------------------------------------------------------------- 1 | #import "SThreadPoolQueue.h" 2 | 3 | #import "SThreadPool.h" 4 | 5 | @interface SThreadPoolQueue () 6 | { 7 | __weak SThreadPool *_threadPool; 8 | NSMutableArray *_tasks; 9 | } 10 | 11 | @end 12 | 13 | @implementation SThreadPoolQueue 14 | 15 | - (instancetype)initWithThreadPool:(SThreadPool *)threadPool 16 | { 17 | self = [super init]; 18 | if (self != nil) 19 | { 20 | _threadPool = threadPool; 21 | _tasks = [[NSMutableArray alloc] init]; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)addTask:(SThreadPoolTask *)task 27 | { 28 | SThreadPool *threadPool = _threadPool; 29 | [threadPool _workOnQueue:self block:^ 30 | { 31 | [_tasks addObject:task]; 32 | }]; 33 | } 34 | 35 | - (SThreadPoolTask *)_popFirstTask 36 | { 37 | if (_tasks.count != 0) 38 | { 39 | SThreadPoolTask *task = _tasks[0]; 40 | [_tasks removeObjectAtIndex:0]; 41 | return task; 42 | } 43 | return nil; 44 | } 45 | 46 | - (bool)_hasTasks 47 | { 48 | return _tasks.count != 0; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPoolTask.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SThreadPoolTask : NSObject 4 | 5 | - (instancetype)initWithBlock:(void (^)(bool (^)()))block; 6 | - (void)execute; 7 | - (void)cancel; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /SSignalKit/SThreadPoolTask.m: -------------------------------------------------------------------------------- 1 | #import "SThreadPoolTask.h" 2 | 3 | @interface SThreadPoolTaskState : NSObject 4 | { 5 | @public 6 | bool _cancelled; 7 | } 8 | 9 | @end 10 | 11 | @implementation SThreadPoolTaskState 12 | 13 | @end 14 | 15 | @interface SThreadPoolTask () 16 | { 17 | void (^_block)(bool (^)()); 18 | SThreadPoolTaskState *_state; 19 | } 20 | 21 | @end 22 | 23 | @implementation SThreadPoolTask 24 | 25 | - (instancetype)initWithBlock:(void (^)(bool (^)()))block 26 | { 27 | self = [super init]; 28 | if (self != nil) 29 | { 30 | _block = [block copy]; 31 | _state = [[SThreadPoolTaskState alloc] init]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)execute 37 | { 38 | if (_state->_cancelled) 39 | return; 40 | 41 | SThreadPoolTaskState *state = _state; 42 | _block(^bool 43 | { 44 | return state->_cancelled; 45 | }); 46 | } 47 | 48 | - (void)cancel 49 | { 50 | _state->_cancelled = true; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /SSignalKit/STimer.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SQueue; 4 | 5 | @interface STimer : NSObject 6 | 7 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue; 8 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue; 9 | 10 | - (void)start; 11 | - (void)invalidate; 12 | - (void)fireAndInvalidate; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SSignalKit/STimer.m: -------------------------------------------------------------------------------- 1 | #import "STimer.h" 2 | 3 | #import "SQueue.h" 4 | 5 | @interface STimer () 6 | { 7 | dispatch_source_t _timer; 8 | NSTimeInterval _timeout; 9 | NSTimeInterval _timeoutDate; 10 | bool _repeat; 11 | dispatch_block_t _completion; 12 | dispatch_queue_t _nativeQueue; 13 | } 14 | 15 | @end 16 | 17 | @implementation STimer 18 | 19 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue { 20 | return [self initWithTimeout:timeout repeat:repeat completion:completion nativeQueue:queue._dispatch_queue]; 21 | } 22 | 23 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue 24 | { 25 | self = [super init]; 26 | if (self != nil) 27 | { 28 | _timeoutDate = INT_MAX; 29 | 30 | _timeout = timeout; 31 | _repeat = repeat; 32 | _completion = [completion copy]; 33 | _nativeQueue = nativeQueue; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)dealloc 39 | { 40 | if (_timer != nil) 41 | { 42 | dispatch_source_cancel(_timer); 43 | _timer = nil; 44 | } 45 | } 46 | 47 | - (void)start 48 | { 49 | _timeoutDate = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 + _timeout; 50 | 51 | _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _nativeQueue); 52 | dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), _repeat ? (int64_t)(_timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER, 0); 53 | 54 | dispatch_source_set_event_handler(_timer, ^ 55 | { 56 | if (_completion) 57 | _completion(); 58 | if (!_repeat) 59 | [self invalidate]; 60 | }); 61 | dispatch_resume(_timer); 62 | } 63 | 64 | - (void)fireAndInvalidate 65 | { 66 | if (_completion) 67 | _completion(); 68 | 69 | [self invalidate]; 70 | } 71 | 72 | - (void)invalidate 73 | { 74 | _timeoutDate = 0; 75 | 76 | if (_timer != nil) 77 | { 78 | dispatch_source_cancel(_timer); 79 | _timer = nil; 80 | } 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /SSignalKit/SVariable.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SSignal; 4 | 5 | @interface SVariable : NSObject 6 | 7 | - (instancetype)init; 8 | 9 | - (void)set:(SSignal *)signal; 10 | - (SSignal *)signal; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /SSignalKit/SVariable.m: -------------------------------------------------------------------------------- 1 | #import "SVariable.h" 2 | 3 | #import 4 | 5 | #import "SSignal.h" 6 | #import "SBag.h" 7 | #import "SBlockDisposable.h" 8 | #import "SMetaDisposable.h" 9 | 10 | @interface SVariable () 11 | { 12 | OSSpinLock _lock; 13 | id _value; 14 | bool _hasValue; 15 | SBag *_subscribers; 16 | SMetaDisposable *_disposable; 17 | } 18 | 19 | @end 20 | 21 | @implementation SVariable 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self != nil) 27 | { 28 | _subscribers = [[SBag alloc] init]; 29 | _disposable = [[SMetaDisposable alloc] init]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)dealloc 35 | { 36 | [_disposable dispose]; 37 | } 38 | 39 | - (SSignal *)signal 40 | { 41 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 42 | { 43 | OSSpinLockLock(&self->_lock); 44 | id currentValue = _value; 45 | bool hasValue = _hasValue; 46 | NSInteger index = [self->_subscribers addItem:[^(id value) 47 | { 48 | [subscriber putNext:value]; 49 | } copy]]; 50 | OSSpinLockUnlock(&self->_lock); 51 | 52 | if (hasValue) 53 | { 54 | [subscriber putNext:currentValue]; 55 | } 56 | 57 | return [[SBlockDisposable alloc] initWithBlock:^ 58 | { 59 | OSSpinLockLock(&self->_lock); 60 | [self->_subscribers removeItem:index]; 61 | OSSpinLockUnlock(&self->_lock); 62 | }]; 63 | }]; 64 | } 65 | 66 | - (void)set:(SSignal *)signal 67 | { 68 | OSSpinLockLock(&_lock); 69 | _hasValue = false; 70 | OSSpinLockUnlock(&_lock); 71 | 72 | __weak SVariable *weakSelf = self; 73 | [_disposable setDisposable:[signal startWithNext:^(id next) 74 | { 75 | __strong SVariable *strongSelf = weakSelf; 76 | if (strongSelf != nil) 77 | { 78 | NSArray *subscribers = nil; 79 | OSSpinLockLock(&strongSelf->_lock); 80 | strongSelf->_value = next; 81 | strongSelf->_hasValue = true; 82 | subscribers = [strongSelf->_subscribers copyItems]; 83 | OSSpinLockUnlock(&strongSelf->_lock); 84 | 85 | for (void (^subscriber)(id) in subscribers) 86 | { 87 | subscriber(next); 88 | } 89 | } 90 | }]]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /SSignalKitTests/DeallocatingObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface DeallocatingObject : NSObject 4 | 5 | - (instancetype)initWithDeallocated:(bool *)deallocated; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /SSignalKitTests/DeallocatingObject.m: -------------------------------------------------------------------------------- 1 | #import "DeallocatingObject.h" 2 | 3 | @interface DeallocatingObject () 4 | { 5 | bool *_deallocated; 6 | } 7 | 8 | @end 9 | 10 | @implementation DeallocatingObject 11 | 12 | - (instancetype)initWithDeallocated:(bool *)deallocated 13 | { 14 | self = [super init]; 15 | if (self != nil) 16 | { 17 | _deallocated = deallocated; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)dealloc 23 | { 24 | *_deallocated = true; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SSignalKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SSignalKitTests/SSignalPerformanceTests.m: -------------------------------------------------------------------------------- 1 | #if __IPHONE_OS_VERSION_MIN_REQUIRED 2 | #import 3 | #else 4 | #import 5 | #endif 6 | #import 7 | 8 | @import SSignalKit; 9 | 10 | @interface SSignalPerformanceTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation SSignalPerformanceTests 15 | 16 | - (void)setUp 17 | { 18 | [super setUp]; 19 | } 20 | 21 | - (void)tearDown 22 | { 23 | [super tearDown]; 24 | } 25 | 26 | - (void)testMap 27 | { 28 | [self measureBlock:^ 29 | { 30 | SSignal *signal = [[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) 31 | { 32 | [subscriber putNext:@1]; 33 | [subscriber putCompletion]; 34 | return nil; 35 | }] map:^id (id value) 36 | { 37 | return value; 38 | }]; 39 | 40 | for (int i = 0; i < 100000; i++) 41 | { 42 | [signal startWithNext:^(__unused id next) 43 | { 44 | 45 | }]; 46 | } 47 | }]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /SwiftSignalKit copy-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftSignalKit/Atomic.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Atomic { 4 | private var lock: pthread_mutex_t 5 | private var value: T 6 | 7 | public init(value: T) { 8 | self.lock = pthread_mutex_t() 9 | self.value = value 10 | 11 | pthread_mutex_init(&self.lock, nil) 12 | } 13 | 14 | deinit { 15 | pthread_mutex_destroy(&self.lock) 16 | } 17 | 18 | public func with(_ f: (T) -> R) -> R { 19 | pthread_mutex_lock(&self.lock) 20 | let result = f(self.value) 21 | pthread_mutex_unlock(&self.lock) 22 | 23 | return result 24 | } 25 | 26 | public func modify(_ f: (T) -> T) -> T { 27 | pthread_mutex_lock(&self.lock) 28 | let result = f(self.value) 29 | self.value = result 30 | pthread_mutex_unlock(&self.lock) 31 | 32 | return result 33 | } 34 | 35 | public func swap(_ value: T) -> T { 36 | pthread_mutex_lock(&self.lock) 37 | let previous = self.value 38 | self.value = value 39 | pthread_mutex_unlock(&self.lock) 40 | 41 | return previous 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SwiftSignalKit/Bag.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Bag { 4 | public typealias Index = Int 5 | private var nextIndex: Index = 0 6 | private var items: [T] = [] 7 | private var itemKeys: [Index] = [] 8 | 9 | public init() { 10 | } 11 | 12 | public func add(_ item: T) -> Index { 13 | let key = self.nextIndex 14 | self.nextIndex += 1 15 | self.items.append(item) 16 | self.itemKeys.append(key) 17 | 18 | return key 19 | } 20 | 21 | public func get(_ index: Index) -> T? { 22 | var i = 0 23 | for key in self.itemKeys { 24 | if key == index { 25 | return self.items[i] 26 | } 27 | i += 1 28 | } 29 | return nil 30 | } 31 | 32 | public func remove(_ index: Index) { 33 | var i = 0 34 | for key in self.itemKeys { 35 | if key == index { 36 | self.items.remove(at: i) 37 | self.itemKeys.remove(at: i) 38 | break 39 | } 40 | i += 1 41 | } 42 | } 43 | 44 | public func removeAll() { 45 | self.items.removeAll() 46 | self.itemKeys.removeAll() 47 | } 48 | 49 | public func copyItems() -> [T] { 50 | return self.items 51 | } 52 | 53 | public func copyItemsWithIndices() -> [(Index, T)] { 54 | var result: [(Index, T)] = [] 55 | var i = 0 56 | for key in self.itemKeys { 57 | result.append((key, self.items[i])) 58 | i += 1 59 | } 60 | return result 61 | } 62 | 63 | public var isEmpty: Bool { 64 | return self.items.isEmpty 65 | } 66 | 67 | public var first: (Index, T)? { 68 | if !self.items.isEmpty { 69 | return (self.itemKeys[0], self.items[0]) 70 | } else { 71 | return nil 72 | } 73 | } 74 | } 75 | 76 | public final class CounterBag { 77 | private var nextIndex: Int = 1 78 | private var items = Set() 79 | 80 | public init() { 81 | } 82 | 83 | public func add() -> Int { 84 | let index = self.nextIndex 85 | self.nextIndex += 1 86 | self.items.insert(index) 87 | return index 88 | } 89 | 90 | public func remove(_ index: Int) { 91 | self.items.remove(index) 92 | } 93 | 94 | public var isEmpty: Bool { 95 | return self.items.isEmpty 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /SwiftSignalKit/Disposable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Disposable: class { 4 | func dispose() 5 | } 6 | 7 | final class _EmptyDisposable: Disposable { 8 | func dispose() { 9 | } 10 | } 11 | 12 | public let EmptyDisposable: Disposable = _EmptyDisposable() 13 | 14 | public final class ActionDisposable : Disposable { 15 | private var lock = pthread_mutex_t() 16 | 17 | private var action: (() -> Void)? 18 | 19 | public init(action: @escaping() -> Void) { 20 | self.action = action 21 | 22 | pthread_mutex_init(&self.lock, nil) 23 | } 24 | 25 | deinit { 26 | var freeAction: (() -> Void)? 27 | pthread_mutex_lock(&self.lock) 28 | freeAction = self.action 29 | self.action = nil 30 | pthread_mutex_unlock(&self.lock) 31 | 32 | if let freeAction = freeAction { 33 | withExtendedLifetime(freeAction, {}) 34 | } 35 | 36 | pthread_mutex_destroy(&self.lock) 37 | } 38 | 39 | public func dispose() { 40 | let disposeAction: (() -> Void)? 41 | 42 | pthread_mutex_lock(&self.lock) 43 | disposeAction = self.action 44 | self.action = nil 45 | pthread_mutex_unlock(&self.lock) 46 | 47 | disposeAction?() 48 | } 49 | } 50 | 51 | public final class MetaDisposable : Disposable { 52 | private var lock = pthread_mutex_t() 53 | private var disposed = false 54 | private var disposable: Disposable! = nil 55 | 56 | public init() { 57 | pthread_mutex_init(&self.lock, nil) 58 | } 59 | 60 | deinit { 61 | var freeDisposable: Disposable? 62 | pthread_mutex_lock(&self.lock) 63 | if let disposable = self.disposable { 64 | freeDisposable = disposable 65 | self.disposable = nil 66 | } 67 | pthread_mutex_unlock(&self.lock) 68 | if let freeDisposable = freeDisposable { 69 | withExtendedLifetime(freeDisposable, { }) 70 | } 71 | 72 | pthread_mutex_destroy(&self.lock) 73 | } 74 | 75 | public func set(_ disposable: Disposable?) { 76 | var previousDisposable: Disposable! = nil 77 | var disposeImmediately = false 78 | 79 | pthread_mutex_lock(&self.lock) 80 | disposeImmediately = self.disposed 81 | if !disposeImmediately { 82 | previousDisposable = self.disposable 83 | if let disposable = disposable { 84 | self.disposable = disposable 85 | } else { 86 | self.disposable = nil 87 | } 88 | } 89 | pthread_mutex_unlock(&self.lock) 90 | 91 | if previousDisposable != nil { 92 | previousDisposable.dispose() 93 | } 94 | 95 | if disposeImmediately { 96 | if let disposable = disposable { 97 | disposable.dispose() 98 | } 99 | } 100 | } 101 | 102 | public func dispose() 103 | { 104 | var disposable: Disposable! = nil 105 | 106 | pthread_mutex_lock(&self.lock) 107 | if !self.disposed { 108 | self.disposed = true 109 | disposable = self.disposable 110 | self.disposable = nil 111 | } 112 | pthread_mutex_unlock(&self.lock) 113 | 114 | if disposable != nil { 115 | disposable.dispose() 116 | } 117 | } 118 | } 119 | 120 | public final class DisposableSet : Disposable { 121 | private var lock = pthread_mutex_t() 122 | private var disposed = false 123 | private var disposables: [Disposable] = [] 124 | 125 | public init() { 126 | pthread_mutex_init(&self.lock, nil) 127 | } 128 | 129 | deinit { 130 | pthread_mutex_lock(&self.lock) 131 | self.disposables.removeAll() 132 | pthread_mutex_unlock(&self.lock) 133 | 134 | pthread_mutex_destroy(&self.lock) 135 | } 136 | 137 | public func add(_ disposable: Disposable) { 138 | var disposeImmediately = false 139 | 140 | pthread_mutex_lock(&self.lock) 141 | if self.disposed { 142 | disposeImmediately = true 143 | } else { 144 | self.disposables.append(disposable) 145 | } 146 | pthread_mutex_unlock(&self.lock) 147 | 148 | if disposeImmediately { 149 | disposable.dispose() 150 | } 151 | } 152 | 153 | public func remove(_ disposable: Disposable) { 154 | pthread_mutex_lock(&self.lock) 155 | if let index = self.disposables.index(where: { $0 === disposable }) { 156 | self.disposables.remove(at: index) 157 | } 158 | pthread_mutex_unlock(&self.lock) 159 | } 160 | 161 | public func dispose() { 162 | var disposables: [Disposable] = [] 163 | pthread_mutex_lock(&self.lock) 164 | if !self.disposed { 165 | self.disposed = true 166 | disposables = self.disposables 167 | self.disposables = [] 168 | } 169 | pthread_mutex_unlock(&self.lock) 170 | 171 | if disposables.count != 0 { 172 | for disposable in disposables { 173 | disposable.dispose() 174 | } 175 | } 176 | } 177 | } 178 | 179 | public final class DisposableDict : Disposable { 180 | private var lock = pthread_mutex_t() 181 | private var disposed = false 182 | private var disposables: [T: Disposable] = [:] 183 | 184 | public init() { 185 | pthread_mutex_init(&self.lock, nil) 186 | } 187 | 188 | deinit { 189 | pthread_mutex_lock(&self.lock) 190 | self.disposables.removeAll() 191 | pthread_mutex_unlock(&self.lock) 192 | 193 | pthread_mutex_destroy(&self.lock) 194 | } 195 | 196 | public func set(_ disposable: Disposable?, forKey key: T) { 197 | var disposeImmediately = false 198 | var disposePrevious: Disposable? 199 | 200 | pthread_mutex_lock(&self.lock) 201 | if self.disposed { 202 | disposeImmediately = true 203 | } else { 204 | disposePrevious = self.disposables[key] 205 | if let disposable = disposable { 206 | self.disposables[key] = disposable 207 | } 208 | } 209 | pthread_mutex_unlock(&self.lock) 210 | 211 | if disposeImmediately { 212 | disposable?.dispose() 213 | } 214 | disposePrevious?.dispose() 215 | } 216 | 217 | public func dispose() { 218 | var disposables: [T: Disposable] = [:] 219 | pthread_mutex_lock(&self.lock) 220 | if !self.disposed { 221 | self.disposed = true 222 | disposables = self.disposables 223 | self.disposables = [:] 224 | } 225 | pthread_mutex_unlock(&self.lock) 226 | 227 | if disposables.count != 0 { 228 | for disposable in disposables.values { 229 | disposable.dispose() 230 | } 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /SwiftSignalKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftSignalKit/Lock.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Lock { 4 | private var mutex = pthread_mutex_t() 5 | 6 | public init() { 7 | pthread_mutex_init(&self.mutex, nil) 8 | } 9 | 10 | deinit { 11 | pthread_mutex_destroy(&self.mutex) 12 | } 13 | 14 | public func locked(_ f: () -> ()) { 15 | pthread_mutex_lock(&self.mutex) 16 | f() 17 | pthread_mutex_unlock(&self.mutex) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SwiftSignalKit/Multicast.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private final class MulticastInstance { 4 | let disposable: Disposable 5 | var subscribers = Bag<(T) -> Void>() 6 | var lock = Lock() 7 | 8 | init(disposable: Disposable) { 9 | self.disposable = disposable 10 | } 11 | } 12 | 13 | public final class Multicast { 14 | private let lock = Lock() 15 | private var instances: [String: MulticastInstance] = [:] 16 | 17 | public init() { 18 | } 19 | 20 | public func get(key: String, signal: Signal) -> Signal { 21 | return Signal { subscriber in 22 | var instance: MulticastInstance! 23 | var beginDisposable: MetaDisposable? 24 | self.lock.locked { 25 | if let existing = self.instances[key] { 26 | instance = existing 27 | } else { 28 | let disposable = MetaDisposable() 29 | instance = MulticastInstance(disposable: disposable) 30 | beginDisposable = disposable 31 | } 32 | } 33 | 34 | var index: Bag<(T) -> Void>.Index! 35 | instance.lock.locked { 36 | index = instance.subscribers.add({ next in 37 | subscriber.putNext(next) 38 | }) 39 | } 40 | 41 | if let beginDisposable = beginDisposable { 42 | beginDisposable.set(signal.start(next: { next in 43 | var subscribers: [(T) -> Void]! 44 | instance.lock.locked { 45 | subscribers = instance.subscribers.copyItems() 46 | } 47 | for subscriber in subscribers { 48 | subscriber(next) 49 | } 50 | }, error: { _ in 51 | self.lock.locked { 52 | let _ = self.instances.removeValue(forKey: key) 53 | } 54 | }, completed: { 55 | self.lock.locked { 56 | self.instances.removeValue(forKey: key) 57 | } 58 | })) 59 | } 60 | 61 | return ActionDisposable { 62 | var remove = false 63 | instance.lock.locked { 64 | instance.subscribers.remove(index) 65 | if instance.subscribers.isEmpty { 66 | remove = true 67 | } 68 | } 69 | 70 | if remove { 71 | self.lock.locked { 72 | let _ = self.instances.removeValue(forKey: key) 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | public final class MulticastPromise { 81 | public let subscribers = Bag<(T) -> Void>() 82 | public let lock = Lock() 83 | public var value: T? 84 | 85 | public init() { 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /SwiftSignalKit/Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Promise { 4 | private var initializeOnFirstAccess: Signal? 5 | private var value: T? 6 | private var lock = pthread_mutex_t() 7 | private let disposable = MetaDisposable() 8 | private let subscribers = Bag<(T) -> Void>() 9 | 10 | public var onDeinit: (() -> Void)? 11 | 12 | public init(initializeOnFirstAccess: Signal?) { 13 | self.initializeOnFirstAccess = initializeOnFirstAccess 14 | pthread_mutex_init(&self.lock, nil) 15 | } 16 | 17 | public init(_ value: T) { 18 | self.value = value 19 | pthread_mutex_init(&self.lock, nil) 20 | } 21 | 22 | public init() { 23 | pthread_mutex_init(&self.lock, nil) 24 | } 25 | 26 | deinit { 27 | self.onDeinit?() 28 | pthread_mutex_destroy(&self.lock) 29 | self.disposable.dispose() 30 | } 31 | 32 | public func set(_ signal: Signal) { 33 | pthread_mutex_lock(&self.lock) 34 | self.value = nil 35 | pthread_mutex_unlock(&self.lock) 36 | 37 | self.disposable.set(signal.start(next: { [weak self] next in 38 | if let strongSelf = self { 39 | pthread_mutex_lock(&strongSelf.lock) 40 | strongSelf.value = next 41 | let subscribers = strongSelf.subscribers.copyItems() 42 | pthread_mutex_unlock(&strongSelf.lock) 43 | 44 | for subscriber in subscribers { 45 | subscriber(next) 46 | } 47 | } 48 | })) 49 | } 50 | 51 | public func get() -> Signal { 52 | return Signal { subscriber in 53 | pthread_mutex_lock(&self.lock) 54 | var initializeOnFirstAccessNow: Signal? 55 | if let initializeOnFirstAccess = self.initializeOnFirstAccess { 56 | initializeOnFirstAccessNow = initializeOnFirstAccess 57 | self.initializeOnFirstAccess = nil 58 | } 59 | let currentValue = self.value 60 | let index = self.subscribers.add({ next in 61 | subscriber.putNext(next) 62 | }) 63 | pthread_mutex_unlock(&self.lock) 64 | 65 | if let currentValue = currentValue { 66 | subscriber.putNext(currentValue) 67 | } 68 | 69 | if let initializeOnFirstAccessNow = initializeOnFirstAccessNow { 70 | self.set(initializeOnFirstAccessNow) 71 | } 72 | 73 | return ActionDisposable { 74 | pthread_mutex_lock(&self.lock) 75 | self.subscribers.remove(index) 76 | pthread_mutex_unlock(&self.lock) 77 | } 78 | } 79 | } 80 | } 81 | 82 | public final class ValuePromise { 83 | private var value: T? 84 | private var lock = pthread_mutex_t() 85 | private let subscribers = Bag<(T) -> Void>() 86 | public let ignoreRepeated: Bool 87 | 88 | public init(_ value: T, ignoreRepeated: Bool = false) { 89 | self.value = value 90 | self.ignoreRepeated = ignoreRepeated 91 | pthread_mutex_init(&self.lock, nil) 92 | } 93 | 94 | public init(ignoreRepeated: Bool = false) { 95 | self.ignoreRepeated = ignoreRepeated 96 | pthread_mutex_init(&self.lock, nil) 97 | } 98 | 99 | deinit { 100 | pthread_mutex_destroy(&self.lock) 101 | } 102 | 103 | public func set(_ value: T) { 104 | pthread_mutex_lock(&self.lock) 105 | let subscribers: [(T) -> Void] 106 | if !self.ignoreRepeated || self.value != value { 107 | self.value = value 108 | subscribers = self.subscribers.copyItems() 109 | } else { 110 | subscribers = [] 111 | } 112 | pthread_mutex_unlock(&self.lock); 113 | 114 | for subscriber in subscribers { 115 | subscriber(value) 116 | } 117 | } 118 | 119 | public func get() -> Signal { 120 | return Signal { subscriber in 121 | pthread_mutex_lock(&self.lock) 122 | let currentValue = self.value 123 | let index = self.subscribers.add({ next in 124 | subscriber.putNext(next) 125 | }) 126 | pthread_mutex_unlock(&self.lock) 127 | 128 | if let currentValue = currentValue { 129 | subscriber.putNext(currentValue) 130 | } 131 | 132 | return ActionDisposable { 133 | pthread_mutex_lock(&self.lock) 134 | self.subscribers.remove(index) 135 | pthread_mutex_unlock(&self.lock) 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /SwiftSignalKit/Queue.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private let QueueSpecificKey = DispatchSpecificKey() 4 | 5 | private let globalMainQueue = Queue(queue: DispatchQueue.main, specialIsMainQueue: true) 6 | private let globalDefaultQueue = Queue(queue: DispatchQueue.global(qos: .default), specialIsMainQueue: false) 7 | private let globalBackgroundQueue = Queue(queue: DispatchQueue.global(qos: .background), specialIsMainQueue: false) 8 | 9 | public final class Queue { 10 | private let nativeQueue: DispatchQueue 11 | private var specific = NSObject() 12 | private let specialIsMainQueue: Bool 13 | 14 | public var queue: DispatchQueue { 15 | get { 16 | return self.nativeQueue 17 | } 18 | } 19 | 20 | public class func mainQueue() -> Queue { 21 | return globalMainQueue 22 | } 23 | 24 | public class func concurrentDefaultQueue() -> Queue { 25 | return globalDefaultQueue 26 | } 27 | 28 | public class func concurrentBackgroundQueue() -> Queue { 29 | return globalBackgroundQueue 30 | } 31 | 32 | public init(queue: DispatchQueue) { 33 | self.nativeQueue = queue 34 | self.specialIsMainQueue = false 35 | } 36 | 37 | fileprivate init(queue: DispatchQueue, specialIsMainQueue: Bool) { 38 | self.nativeQueue = queue 39 | self.specialIsMainQueue = specialIsMainQueue 40 | } 41 | 42 | public init(name: String? = nil, qos: DispatchQoS = .default) { 43 | self.nativeQueue = DispatchQueue(label: name ?? "", qos: qos) 44 | 45 | self.specialIsMainQueue = false 46 | 47 | self.nativeQueue.setSpecific(key: QueueSpecificKey, value: self.specific) 48 | } 49 | 50 | public func isCurrent() -> Bool { 51 | if DispatchQueue.getSpecific(key: QueueSpecificKey) === self.specific { 52 | return true 53 | } else if self.specialIsMainQueue && Thread.isMainThread { 54 | return true 55 | } else { 56 | return false 57 | } 58 | } 59 | 60 | public func async(_ f: @escaping () -> Void) { 61 | if self.isCurrent() { 62 | f() 63 | } else { 64 | self.nativeQueue.async(execute: f) 65 | } 66 | } 67 | 68 | public func sync(_ f: () -> Void) { 69 | if self.isCurrent() { 70 | f() 71 | } else { 72 | self.nativeQueue.sync(execute: f) 73 | } 74 | } 75 | 76 | public func justDispatch(_ f: @escaping () -> Void) { 77 | self.nativeQueue.async(execute: f) 78 | } 79 | 80 | public func justDispatchWithQoS(qos: DispatchQoS, _ f: @escaping () -> Void) { 81 | self.nativeQueue.async(group: nil, qos: qos, flags: [.enforceQoS], execute: f) 82 | } 83 | 84 | public func after(_ delay: Double, _ f: @escaping() -> Void) { 85 | let time: DispatchTime = DispatchTime.now() + delay 86 | self.nativeQueue.asyncAfter(deadline: time, execute: f) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /SwiftSignalKit/QueueLocalObject.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class QueueLocalObject { 4 | private let queue: Queue 5 | private var valueRef: Unmanaged? 6 | 7 | public init(queue: Queue, generate: @escaping () -> T) { 8 | self.queue = queue 9 | 10 | self.queue.async { 11 | let value = generate() 12 | self.valueRef = Unmanaged.passRetained(value) 13 | } 14 | } 15 | 16 | deinit { 17 | let valueRef = self.valueRef 18 | self.queue.async { 19 | valueRef?.release() 20 | } 21 | } 22 | 23 | public func with(_ f: @escaping (T) -> Void) { 24 | self.queue.async { 25 | if let valueRef = self.valueRef { 26 | let value = valueRef.takeUnretainedValue() 27 | f(value) 28 | } 29 | } 30 | } 31 | 32 | public func syncWith(_ f: @escaping (T) -> R) -> R? { 33 | var result: R? 34 | self.queue.sync { 35 | if let valueRef = self.valueRef { 36 | let value = valueRef.takeUnretainedValue() 37 | result = f(value) 38 | } 39 | } 40 | return result 41 | } 42 | 43 | public func signalWith(_ f: @escaping (T, Subscriber) -> Disposable) -> Signal { 44 | return Signal { [weak self] subscriber in 45 | if let strongSelf = self, let valueRef = strongSelf.valueRef { 46 | let value = valueRef.takeUnretainedValue() 47 | return f(value, subscriber) 48 | } else { 49 | return EmptyDisposable 50 | } 51 | } |> runOn(self.queue) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let doNothing: () -> Void = { } 4 | 5 | public enum NoValue { 6 | } 7 | 8 | public enum NoError { 9 | } 10 | 11 | public func identity(a: A) -> A { 12 | return a 13 | } 14 | 15 | precedencegroup PipeRight { 16 | associativity: left 17 | higherThan: DefaultPrecedence 18 | } 19 | 20 | infix operator |> : PipeRight 21 | 22 | public func |> (value: T, function: ((T) -> U)) -> U { 23 | return function(value) 24 | } 25 | 26 | private final class SubscriberDisposable : Disposable { 27 | private let subscriber: Subscriber 28 | private let disposable: Disposable 29 | 30 | init(subscriber: Subscriber, disposable: Disposable) { 31 | self.subscriber = subscriber 32 | self.disposable = disposable 33 | } 34 | 35 | func dispose() { 36 | subscriber.markTerminatedWithoutDisposal() 37 | disposable.dispose() 38 | } 39 | } 40 | 41 | public struct Signal { 42 | private let generator: (Subscriber) -> Disposable 43 | 44 | public init(_ generator: @escaping(Subscriber) -> Disposable) { 45 | self.generator = generator 46 | } 47 | 48 | public func start(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable { 49 | let subscriber = Subscriber(next: next, error: error, completed: completed) 50 | let disposable = self.generator(subscriber) 51 | subscriber.assignDisposable(disposable) 52 | return SubscriberDisposable(subscriber: subscriber, disposable: disposable) 53 | } 54 | 55 | public static func single(_ value: T) -> Signal { 56 | return Signal { subscriber in 57 | subscriber.putNext(value) 58 | subscriber.putCompletion() 59 | 60 | return EmptyDisposable 61 | } 62 | } 63 | 64 | public static func complete() -> Signal { 65 | return Signal { subscriber in 66 | subscriber.putCompletion() 67 | 68 | return EmptyDisposable 69 | } 70 | } 71 | 72 | public static func fail(_ error: E) -> Signal { 73 | return Signal { subscriber in 74 | subscriber.putError(error) 75 | 76 | return EmptyDisposable 77 | } 78 | } 79 | 80 | public static func never() -> Signal { 81 | return Signal { _ in 82 | return EmptyDisposable 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Catch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func `catch`(_ f: @escaping(E) -> Signal) -> (Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | let disposable = DisposableSet() 7 | 8 | disposable.add(signal.start(next: { next in 9 | subscriber.putNext(next) 10 | }, error: { error in 11 | let anotherSignal = f(error) 12 | 13 | disposable.add(anotherSignal.start(next: { next in 14 | subscriber.putNext(next) 15 | }, error: { error in 16 | subscriber.putError(error) 17 | }, completed: { 18 | subscriber.putCompletion() 19 | })) 20 | }, completed: { 21 | subscriber.putCompletion() 22 | })) 23 | 24 | return disposable 25 | } 26 | } 27 | } 28 | 29 | private func recursiveFunction(_ f: @escaping(@escaping() -> Void) -> Void) -> (() -> Void) { 30 | return { 31 | f(recursiveFunction(f)) 32 | } 33 | } 34 | 35 | public func restart(_ signal: Signal) -> Signal { 36 | return Signal { subscriber in 37 | let shouldRestart = Atomic(value: true) 38 | let currentDisposable = MetaDisposable() 39 | 40 | let start = recursiveFunction { recurse in 41 | let currentShouldRestart = shouldRestart.with { value in 42 | return value 43 | } 44 | if currentShouldRestart { 45 | let disposable = signal.start(next: { next in 46 | subscriber.putNext(next) 47 | }, error: { error in 48 | subscriber.putError(error) 49 | }, completed: { 50 | recurse() 51 | }) 52 | currentDisposable.set(disposable) 53 | } 54 | } 55 | 56 | start() 57 | 58 | return ActionDisposable { 59 | currentDisposable.dispose() 60 | let _ = shouldRestart.swap(false) 61 | } 62 | } 63 | } 64 | 65 | public func recurse(_ latestValue: T?) -> (Signal) -> Signal { 66 | return { signal in 67 | return Signal { subscriber in 68 | let shouldRestart = Atomic(value: true) 69 | let currentDisposable = MetaDisposable() 70 | 71 | let start = recursiveFunction { recurse in 72 | let currentShouldRestart = shouldRestart.with { value in 73 | return value 74 | } 75 | if currentShouldRestart { 76 | let disposable = signal.start(next: { next in 77 | subscriber.putNext(next) 78 | }, error: { error in 79 | subscriber.putError(error) 80 | }, completed: { 81 | recurse() 82 | }) 83 | currentDisposable.set(disposable) 84 | } 85 | } 86 | 87 | start() 88 | 89 | return ActionDisposable { 90 | currentDisposable.dispose() 91 | let _ = shouldRestart.swap(false) 92 | } 93 | } 94 | } 95 | } 96 | 97 | public func retry(_ delayIncrement: Double, maxDelay: Double, onQueue queue: Queue) -> (_ signal: Signal) -> Signal { 98 | return { signal in 99 | return Signal { subscriber in 100 | let shouldRetry = Atomic(value: true) 101 | let currentDelay = Atomic(value: 0.0) 102 | let currentDisposable = MetaDisposable() 103 | 104 | let start = recursiveFunction { recurse in 105 | let currentShouldRetry = shouldRetry.with { value in 106 | return value 107 | } 108 | if currentShouldRetry { 109 | let disposable = signal.start(next: { next in 110 | subscriber.putNext(next) 111 | }, error: { error in 112 | let delay = currentDelay.modify { value in 113 | return min(maxDelay, value + delayIncrement) 114 | } 115 | 116 | let time: DispatchTime = DispatchTime.now() + Double(delay) 117 | queue.queue.asyncAfter(deadline: time, execute: { 118 | recurse() 119 | }) 120 | }, completed: { 121 | let _ = shouldRetry.swap(false) 122 | subscriber.putCompletion() 123 | }) 124 | currentDisposable.set(disposable) 125 | } 126 | } 127 | 128 | start() 129 | 130 | return ActionDisposable { 131 | currentDisposable.dispose() 132 | let _ = shouldRetry.swap(false) 133 | } 134 | } 135 | } 136 | } 137 | 138 | public func restartIfError(_ signal: Signal) -> Signal { 139 | return Signal { subscriber in 140 | let shouldRetry = Atomic(value: true) 141 | let currentDisposable = MetaDisposable() 142 | 143 | let start = recursiveFunction { recurse in 144 | let currentShouldRetry = shouldRetry.with { value in 145 | return value 146 | } 147 | if currentShouldRetry { 148 | let disposable = signal.start(next: { next in 149 | subscriber.putNext(next) 150 | }, error: { error in 151 | recurse() 152 | }, completed: { 153 | let _ = shouldRetry.swap(false) 154 | subscriber.putCompletion() 155 | }) 156 | currentDisposable.set(disposable) 157 | } 158 | } 159 | 160 | start() 161 | 162 | return ActionDisposable { 163 | currentDisposable.dispose() 164 | let _ = shouldRetry.swap(false) 165 | } 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Combine.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private struct SignalCombineState { 4 | let values: [Int : Any] 5 | let completed: Set 6 | let error: Bool 7 | } 8 | 9 | private func combineLatestAny(_ signals: [Signal], combine: @escaping([Any]) -> R, initialValues: [Int : Any], queue: Queue?) -> Signal { 10 | return Signal { subscriber in 11 | let state = Atomic(value: SignalCombineState(values: initialValues, completed: Set(), error: false)) 12 | let disposable = DisposableSet() 13 | 14 | if initialValues.count == signals.count { 15 | var values: [Any] = [] 16 | for i in 0 ..< initialValues.count { 17 | values.append(initialValues[i]!) 18 | } 19 | subscriber.putNext(combine(values)) 20 | } 21 | 22 | let count = signals.count 23 | for iterationIndex in 0 ..< count { 24 | let index = iterationIndex 25 | var signal = signals[index] 26 | if let queue = queue { 27 | signal = signal 28 | |> deliverOn(queue) 29 | } 30 | let signalDisposable = signal.start(next: { next in 31 | let currentState = state.modify { current in 32 | var values = current.values 33 | values[index] = next 34 | return SignalCombineState(values: values, completed: current.completed, error: current.error) 35 | } 36 | if currentState.values.count == count { 37 | var values: [Any] = [] 38 | for i in 0 ..< count { 39 | values.append(currentState.values[i]!) 40 | } 41 | subscriber.putNext(combine(values)) 42 | } 43 | }, error: { error in 44 | var emitError = false 45 | let _ = state.modify { current in 46 | if !current.error { 47 | emitError = true 48 | return SignalCombineState(values: current.values, completed: current.completed, error: true) 49 | } else { 50 | return current 51 | } 52 | } 53 | if emitError { 54 | subscriber.putError(error) 55 | } 56 | }, completed: { 57 | var emitCompleted = false 58 | let _ = state.modify { current in 59 | if !current.completed.contains(index) { 60 | var completed = current.completed 61 | completed.insert(index) 62 | emitCompleted = completed.count == count 63 | return SignalCombineState(values: current.values, completed: completed, error: current.error) 64 | } 65 | return current 66 | } 67 | if emitCompleted { 68 | subscriber.putCompletion() 69 | } 70 | }) 71 | 72 | disposable.add(signalDisposable) 73 | } 74 | 75 | return disposable; 76 | } 77 | } 78 | 79 | private func signalOfAny(_ signal: Signal) -> Signal { 80 | return Signal { subscriber in 81 | return signal.start(next: { next in 82 | subscriber.putNext(next) 83 | }, error: { error in 84 | subscriber.putError(error) 85 | }, completed: { 86 | subscriber.putCompletion() 87 | }) 88 | } 89 | } 90 | 91 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal) -> Signal<(T1, T2), E> { 92 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in 93 | return (values[0] as! T1, values[1] as! T2) 94 | }, initialValues: [:], queue: queue) 95 | } 96 | 97 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ v1: T1, _ s2: Signal, _ v2: T2) -> Signal<(T1, T2), E> { 98 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in 99 | return (values[0] as! T1, values[1] as! T2) 100 | }, initialValues: [0: v1, 1: v2], queue: queue) 101 | } 102 | 103 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal) -> Signal<(T1, T2, T3), E> { 104 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3)], combine: { values in 105 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3) 106 | }, initialValues: [:], queue: queue) 107 | } 108 | 109 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal) -> Signal<(T1, T2, T3, T4), E> { 110 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4)], combine: { values in 111 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4) 112 | }, initialValues: [:], queue: queue) 113 | } 114 | 115 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal) -> Signal<(T1, T2, T3, T4, T5), E> { 116 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5)], combine: { values in 117 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5) 118 | }, initialValues: [:], queue: queue) 119 | } 120 | 121 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal) -> Signal<(T1, T2, T3, T4, T5, T6), E> { 122 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6)], combine: { values in 123 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6) 124 | }, initialValues: [:], queue: queue) 125 | } 126 | 127 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal, _ s7: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7), E> { 128 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7)], combine: { values in 129 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7) 130 | }, initialValues: [:], queue: queue) 131 | } 132 | 133 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal, _ s7: Signal, _ s8: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8), E> { 134 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8)], combine: { values in 135 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8) 136 | }, initialValues: [:], queue: queue) 137 | } 138 | 139 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal, _ s7: Signal, _ s8: Signal, _ s9: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9), E> { 140 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9)], combine: { values in 141 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9) 142 | }, initialValues: [:], queue: queue) 143 | } 144 | 145 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal, _ s5: Signal, _ s6: Signal, _ s7: Signal, _ s8: Signal, _ s9: Signal, _ s10: Signal) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), E> { 146 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10)], combine: { values in 147 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10) 148 | }, initialValues: [:], queue: queue) 149 | } 150 | 151 | public func combineLatest(queue: Queue? = nil, _ signals: [Signal]) -> Signal<[T], E> { 152 | if signals.count == 0 { 153 | return single([T](), E.self) 154 | } 155 | 156 | return combineLatestAny(signals.map({signalOfAny($0)}), combine: { values in 157 | var combined: [T] = [] 158 | for value in values { 159 | combined.append(value as! T) 160 | } 161 | return combined 162 | }, initialValues: [:], queue: queue) 163 | } 164 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Dispatch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func deliverOn(_ queue: Queue) -> (Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | return signal.start(next: { next in 7 | queue.async { 8 | subscriber.putNext(next) 9 | } 10 | }, error: { error in 11 | queue.async { 12 | subscriber.putError(error) 13 | } 14 | }, completed: { 15 | queue.async { 16 | subscriber.putCompletion() 17 | } 18 | }) 19 | } 20 | } 21 | } 22 | 23 | public func deliverOnMainQueue(_ signal: Signal) -> Signal { 24 | return signal |> deliverOn(Queue.mainQueue()) 25 | } 26 | 27 | public func deliverOn(_ threadPool: ThreadPool) -> (Signal) -> Signal { 28 | return { signal in 29 | return Signal { subscriber in 30 | let queue = threadPool.nextQueue() 31 | return signal.start(next: { next in 32 | queue.addTask(ThreadPoolTask { state in 33 | if !state.cancelled.with { $0 } { 34 | subscriber.putNext(next) 35 | } 36 | }) 37 | }, error: { error in 38 | queue.addTask(ThreadPoolTask { state in 39 | if !state.cancelled.with { $0 } { 40 | subscriber.putError(error) 41 | } 42 | }) 43 | }, completed: { 44 | queue.addTask(ThreadPoolTask { state in 45 | if !state.cancelled.with { $0 } { 46 | subscriber.putCompletion() 47 | } 48 | }) 49 | }) 50 | } 51 | } 52 | } 53 | 54 | public func runOn(_ queue: Queue) -> (Signal) -> Signal { 55 | return { signal in 56 | return Signal { subscriber in 57 | if queue.isCurrent() { 58 | return signal.start(next: { next in 59 | subscriber.putNext(next) 60 | }, error: { error in 61 | subscriber.putError(error) 62 | }, completed: { 63 | subscriber.putCompletion() 64 | }) 65 | } else { 66 | var cancelled = false 67 | let disposable = MetaDisposable() 68 | 69 | disposable.set(ActionDisposable { 70 | cancelled = true 71 | }) 72 | 73 | queue.async { 74 | if cancelled { 75 | return 76 | } 77 | 78 | disposable.set(signal.start(next: { next in 79 | subscriber.putNext(next) 80 | }, error: { error in 81 | subscriber.putError(error) 82 | }, completed: { 83 | subscriber.putCompletion() 84 | })) 85 | } 86 | 87 | return disposable 88 | } 89 | } 90 | } 91 | } 92 | 93 | public func runOn(_ threadPool: ThreadPool) -> (Signal) -> Signal { 94 | return { signal in 95 | return Signal { subscriber in 96 | let cancelled = false 97 | let disposable = MetaDisposable() 98 | 99 | let task = ThreadPoolTask { state in 100 | if cancelled || state.cancelled.with { $0 } { 101 | return 102 | } 103 | 104 | disposable.set(signal.start(next: { next in 105 | subscriber.putNext(next) 106 | }, error: { error in 107 | subscriber.putError(error) 108 | }, completed: { 109 | subscriber.putCompletion() 110 | })) 111 | } 112 | 113 | disposable.set(ActionDisposable { 114 | task.cancel() 115 | }) 116 | 117 | threadPool.addTask(task) 118 | 119 | return disposable 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Loop.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum SignalFeedbackLoopState { 4 | case initial 5 | case loop(T) 6 | } 7 | 8 | public func feedbackLoop(once: @escaping (SignalFeedbackLoopState) -> Signal?, reduce: @escaping (R1, R1) -> R1) -> Signal { 9 | return Signal { subscriber in 10 | let currentDisposable = MetaDisposable() 11 | 12 | let state = Atomic(value: nil) 13 | 14 | var loopAgain: (() -> Void)? 15 | 16 | let loopOnce: (MetaDisposable?) -> Void = { disposable in 17 | if let signal = once(.initial) { 18 | disposable?.set(signal.start(next: { next in 19 | let _ = state.modify { value in 20 | if let value = value { 21 | return reduce(value, next) 22 | } else { 23 | return value 24 | } 25 | } 26 | }, error: { error in 27 | subscriber.putError(error) 28 | }, completed: { 29 | loopAgain?() 30 | })) 31 | } else { 32 | subscriber.putCompletion() 33 | } 34 | } 35 | 36 | loopAgain = { [weak currentDisposable] in 37 | loopOnce(currentDisposable) 38 | } 39 | 40 | loopOnce(currentDisposable) 41 | 42 | return ActionDisposable { 43 | currentDisposable.dispose() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Mapping.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func map(_ f: @escaping(T) -> R) -> (Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | return signal.start(next: { next in 7 | subscriber.putNext(f(next)) 8 | }, error: { error in 9 | subscriber.putError(error) 10 | }, completed: { 11 | subscriber.putCompletion() 12 | }) 13 | } 14 | } 15 | } 16 | 17 | public func filter(_ f: @escaping(T) -> Bool) -> (Signal) -> Signal { 18 | return { signal in 19 | return Signal { subscriber in 20 | return signal.start(next: { next in 21 | if f(next) { 22 | subscriber.putNext(next) 23 | } 24 | }, error: { error in 25 | subscriber.putError(error) 26 | }, completed: { 27 | subscriber.putCompletion() 28 | }) 29 | } 30 | } 31 | } 32 | 33 | public func flatMap(_ f: @escaping (T) -> R?) -> (Signal) -> Signal { 34 | return { signal in 35 | return Signal { subscriber in 36 | return signal.start(next: { next in 37 | if let value = f(next) { 38 | subscriber.putNext(value) 39 | } 40 | }, error: { error in 41 | subscriber.putError(error) 42 | }, completed: { 43 | subscriber.putCompletion() 44 | }) 45 | } 46 | } 47 | } 48 | 49 | public func mapError(_ f: @escaping(E) -> R) -> (Signal) -> Signal { 50 | return { signal in 51 | return Signal { subscriber in 52 | return signal.start(next: { next in 53 | subscriber.putNext(next) 54 | }, error: { error in 55 | subscriber.putError(f(error)) 56 | }, completed: { 57 | subscriber.putCompletion() 58 | }) 59 | } 60 | } 61 | } 62 | 63 | public func introduceError(_ type: E.Type) -> (Signal) -> Signal { 64 | return { signal in 65 | return Signal { subscriber in 66 | return signal.start(next: { next in 67 | subscriber.putNext(next) 68 | }, error: { _ in 69 | }, completed: { 70 | subscriber.putCompletion() 71 | }) 72 | } 73 | } 74 | } 75 | 76 | private class DistinctUntilChangedContext { 77 | var value: T? 78 | } 79 | 80 | public func distinctUntilChanged(_ signal: Signal) -> Signal { 81 | return Signal { subscriber in 82 | let context = Atomic(value: DistinctUntilChangedContext()) 83 | 84 | return signal.start(next: { next in 85 | let pass = context.with { context -> Bool in 86 | if let value = context.value, value == next { 87 | return false 88 | } else { 89 | context.value = next 90 | return true 91 | } 92 | } 93 | if pass { 94 | subscriber.putNext(next) 95 | } 96 | }, error: { error in 97 | subscriber.putError(error) 98 | }, completed: { 99 | subscriber.putCompletion() 100 | }) 101 | } 102 | } 103 | 104 | public func distinctUntilChanged(isEqual: @escaping (T, T) -> Bool) -> (_ signal: Signal) -> Signal { 105 | return { signal in 106 | return Signal { subscriber in 107 | let context = Atomic(value: DistinctUntilChangedContext()) 108 | 109 | return signal.start(next: { next in 110 | let pass = context.with { context -> Bool in 111 | if let value = context.value, isEqual(value, next) { 112 | return false 113 | } else { 114 | context.value = next 115 | return true 116 | } 117 | } 118 | if pass { 119 | subscriber.putNext(next) 120 | } 121 | }, error: { error in 122 | subscriber.putError(error) 123 | }, completed: { 124 | subscriber.putCompletion() 125 | }) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Materialize.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum SignalEvent { 4 | case Next(T) 5 | case Error(E) 6 | case Completion 7 | } 8 | 9 | public func dematerialize(signal: Signal) -> Signal, NoError> { 10 | return Signal { subscriber in 11 | return signal.start(next: { next in 12 | subscriber.putNext(.Next(next)) 13 | }, error: { error in 14 | subscriber.putNext(.Error(error)) 15 | subscriber.putCompletion() 16 | }, completed: { 17 | subscriber.putNext(.Completion) 18 | subscriber.putCompletion() 19 | }) 20 | } 21 | } 22 | 23 | public func materialize(signal: Signal, NoError>) -> Signal { 24 | return Signal { subscriber in 25 | return signal.start(next: { next in 26 | switch next { 27 | case let .Next(next): 28 | subscriber.putNext(next) 29 | case let .Error(error): 30 | subscriber.putError(error) 31 | case .Completion: 32 | subscriber.putCompletion() 33 | } 34 | }, error: { _ in 35 | subscriber.putCompletion() 36 | }, completed: { 37 | subscriber.putCompletion() 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Merge.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /*public func merge(signal1: Signal, signal2: Signal) -> Signal { 4 | return Signal { subscriber in 5 | 6 | } 7 | }*/ 8 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Meta.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private final class SignalQueueState: Disposable { 4 | var lock = pthread_mutex_t() 5 | var executingSignal = false 6 | var terminated = false 7 | 8 | var disposable: Disposable = EmptyDisposable 9 | let currentDisposable = MetaDisposable() 10 | var subscriber: Subscriber? 11 | 12 | var queuedSignals: [Signal] = [] 13 | let queueMode: Bool 14 | let throttleMode: Bool 15 | 16 | init(subscriber: Subscriber, queueMode: Bool, throttleMode: Bool) { 17 | pthread_mutex_init(&self.lock, nil) 18 | 19 | self.subscriber = subscriber 20 | self.queueMode = queueMode 21 | self.throttleMode = throttleMode 22 | } 23 | 24 | deinit { 25 | pthread_mutex_destroy(&self.lock) 26 | } 27 | 28 | func beginWithDisposable(_ disposable: Disposable) { 29 | self.disposable = disposable 30 | } 31 | 32 | func enqueueSignal(_ signal: Signal) { 33 | var startSignal = false 34 | pthread_mutex_lock(&self.lock) 35 | if self.queueMode && self.executingSignal { 36 | if self.throttleMode { 37 | self.queuedSignals.removeAll() 38 | } 39 | self.queuedSignals.append(signal) 40 | } else { 41 | self.executingSignal = true 42 | startSignal = true 43 | } 44 | pthread_mutex_unlock(&self.lock) 45 | 46 | if startSignal { 47 | let disposable = signal.start(next: { next in 48 | assert(self.subscriber != nil) 49 | self.subscriber?.putNext(next) 50 | }, error: { error in 51 | assert(self.subscriber != nil) 52 | self.subscriber?.putError(error) 53 | }, completed: { 54 | self.headCompleted() 55 | }) 56 | self.currentDisposable.set(disposable) 57 | } 58 | } 59 | 60 | func headCompleted() { 61 | while true { 62 | let leftFunction = Atomic(value: false) 63 | 64 | var nextSignal: Signal! = nil 65 | 66 | var terminated = false 67 | pthread_mutex_lock(&self.lock) 68 | self.executingSignal = false 69 | if self.queueMode { 70 | if self.queuedSignals.count != 0 { 71 | nextSignal = self.queuedSignals[0] 72 | self.queuedSignals.remove(at: 0) 73 | self.executingSignal = true 74 | } else { 75 | terminated = self.terminated 76 | } 77 | } else { 78 | terminated = self.terminated 79 | } 80 | pthread_mutex_unlock(&self.lock) 81 | 82 | if terminated { 83 | self.subscriber?.putCompletion() 84 | } else if nextSignal != nil { 85 | let disposable = nextSignal.start(next: { next in 86 | assert(self.subscriber != nil) 87 | self.subscriber?.putNext(next) 88 | }, error: { error in 89 | assert(self.subscriber != nil) 90 | self.subscriber?.putError(error) 91 | }, completed: { 92 | if leftFunction.swap(true) == true { 93 | self.headCompleted() 94 | } 95 | }) 96 | 97 | currentDisposable.set(disposable) 98 | } 99 | 100 | if leftFunction.swap(true) == false { 101 | break 102 | } 103 | } 104 | } 105 | 106 | func beginCompletion() { 107 | var executingSignal = false 108 | pthread_mutex_lock(&self.lock) 109 | executingSignal = self.executingSignal 110 | self.terminated = true 111 | pthread_mutex_unlock(&self.lock) 112 | 113 | if !executingSignal { 114 | self.subscriber?.putCompletion() 115 | } 116 | } 117 | 118 | func dispose() { 119 | self.currentDisposable.dispose() 120 | self.disposable.dispose() 121 | } 122 | } 123 | 124 | public func switchToLatest(_ signal: Signal, E>) -> Signal { 125 | return Signal { subscriber in 126 | let state = SignalQueueState(subscriber: subscriber, queueMode: false, throttleMode: false) 127 | state.beginWithDisposable(signal.start(next: { next in 128 | state.enqueueSignal(next) 129 | }, error: { error in 130 | subscriber.putError(error) 131 | }, completed: { 132 | state.beginCompletion() 133 | })) 134 | return state 135 | } 136 | } 137 | 138 | public func queue(_ signal: Signal, E>) -> Signal { 139 | return Signal { subscriber in 140 | let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: false) 141 | state.beginWithDisposable(signal.start(next: { next in 142 | state.enqueueSignal(next) 143 | }, error: { error in 144 | subscriber.putError(error) 145 | }, completed: { 146 | state.beginCompletion() 147 | })) 148 | return state 149 | } 150 | } 151 | 152 | public func throttled(_ signal: Signal, E>) -> Signal { 153 | return Signal { subscriber in 154 | let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: true) 155 | state.beginWithDisposable(signal.start(next: { next in 156 | state.enqueueSignal(next) 157 | }, error: { error in 158 | subscriber.putError(error) 159 | }, completed: { 160 | state.beginCompletion() 161 | })) 162 | return state 163 | } 164 | } 165 | 166 | public func mapToSignal(_ f: @escaping(T) -> Signal) -> (Signal) -> Signal { 167 | return { signal -> Signal in 168 | return Signal, E> { subscriber in 169 | return signal.start(next: { next in 170 | subscriber.putNext(f(next)) 171 | }, error: { error in 172 | subscriber.putError(error) 173 | }, completed: { 174 | subscriber.putCompletion() 175 | }) 176 | } |> switchToLatest 177 | } 178 | } 179 | 180 | public func ignoreValues(_ signal: Signal) -> Signal { 181 | return Signal { subscriber in 182 | return signal.start(error: { error in 183 | subscriber.putError(error) 184 | }, completed: { 185 | subscriber.putCompletion() 186 | }) 187 | } 188 | } 189 | 190 | public func mapToSignalPromotingError(_ f: @escaping(T) -> Signal) -> (Signal) -> Signal { 191 | return { signal -> Signal in 192 | return Signal, E> { subscriber in 193 | return signal.start(next: { next in 194 | subscriber.putNext(f(next)) 195 | }, completed: { 196 | subscriber.putCompletion() 197 | }) 198 | } |> switchToLatest 199 | } 200 | } 201 | 202 | public func mapToQueue(_ f: @escaping(T) -> Signal) -> (Signal) -> Signal { 203 | return { signal -> Signal in 204 | return signal |> map { f($0) } |> queue 205 | } 206 | } 207 | 208 | public func mapToThrottled(_ f: @escaping(T) -> Signal) -> (Signal) -> Signal { 209 | return { signal -> Signal in 210 | return signal |> map { f($0) } |> throttled 211 | } 212 | } 213 | 214 | public func then(_ nextSignal: Signal) -> (Signal) -> Signal { 215 | return { signal -> Signal in 216 | return Signal { subscriber in 217 | let disposable = DisposableSet() 218 | 219 | disposable.add(signal.start(next: { next in 220 | subscriber.putNext(next) 221 | }, error: { error in 222 | subscriber.putError(error) 223 | }, completed: { 224 | disposable.add(nextSignal.start(next: { next in 225 | subscriber.putNext(next) 226 | }, error: { error in 227 | subscriber.putError(error) 228 | }, completed: { 229 | subscriber.putCompletion() 230 | })) 231 | })) 232 | 233 | return disposable 234 | } 235 | } 236 | } 237 | 238 | public func deferred(_ generator: @escaping() -> Signal) -> Signal { 239 | return Signal { subscriber in 240 | return generator().start(next: { next in 241 | subscriber.putNext(next) 242 | }, error: { error in 243 | subscriber.putError(error) 244 | }, completed: { 245 | subscriber.putCompletion() 246 | }) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Reduce.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func reduceLeft(value: T, f: @escaping(T, T) -> T) -> (_ signal: Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | var currentValue = value 7 | 8 | return signal.start(next: { next in 9 | currentValue = f(currentValue, next) 10 | }, error: { error in 11 | subscriber.putError(error) 12 | }, completed: { 13 | subscriber.putNext(currentValue) 14 | subscriber.putCompletion() 15 | }) 16 | } 17 | } 18 | } 19 | 20 | public func reduceLeft(value: T, f: @escaping(T, T, (T) -> Void) -> T) -> (_ signal: Signal) -> Signal { 21 | return { signal in 22 | return Signal { subscriber in 23 | var currentValue = value 24 | let emit: (T) -> Void = { next in 25 | subscriber.putNext(next) 26 | } 27 | 28 | return signal.start(next: { next in 29 | currentValue = f(currentValue, next, emit) 30 | }, error: { error in 31 | subscriber.putError(error) 32 | }, completed: { 33 | subscriber.putNext(currentValue) 34 | subscriber.putCompletion() 35 | }) 36 | } 37 | } 38 | } 39 | 40 | public enum Passthrough { 41 | case None 42 | case Some(T) 43 | } 44 | 45 | private final class ReduceQueueState : Disposable { 46 | var lock: OSSpinLock = 0 47 | var executingSignal = false 48 | var terminated = false 49 | 50 | var disposable: Disposable = EmptyDisposable 51 | let currentDisposable = MetaDisposable() 52 | let subscriber: Subscriber 53 | 54 | var queuedValues: [T] = [] 55 | var generator: (T, T) -> Signal<(T, Passthrough), E> 56 | var value: T 57 | 58 | init(subscriber: Subscriber, value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough), E>) { 59 | self.subscriber = subscriber 60 | self.generator = generator 61 | self.value = value 62 | } 63 | 64 | func beginWithDisposable(_ disposable: Disposable) { 65 | self.disposable = disposable 66 | } 67 | 68 | func enqueueNext(_ next: T) { 69 | var startSignal = false 70 | var currentValue: T 71 | OSSpinLockLock(&self.lock) 72 | currentValue = self.value 73 | if self.executingSignal { 74 | self.queuedValues.append(next) 75 | } else { 76 | self.executingSignal = true 77 | startSignal = true 78 | } 79 | OSSpinLockUnlock(&self.lock) 80 | 81 | if startSignal { 82 | let disposable = generator(currentValue, next).start(next: { next in 83 | self.updateValue(next.0) 84 | switch next.1 { 85 | case let .Some(value): 86 | self.subscriber.putNext(value) 87 | case .None: 88 | break 89 | } 90 | }, error: { error in 91 | self.subscriber.putError(error) 92 | }, completed: { 93 | self.headCompleted() 94 | }) 95 | self.currentDisposable.set(disposable) 96 | } 97 | } 98 | 99 | func updateValue(_ value: T) { 100 | OSSpinLockLock(&self.lock) 101 | self.value = value 102 | OSSpinLockUnlock(&self.lock) 103 | } 104 | 105 | func headCompleted() { 106 | while true { 107 | let leftFunction = Atomic(value: false) 108 | 109 | var nextSignal: Signal<(T, Passthrough), E>! = nil 110 | 111 | var terminated = false 112 | var currentValue: T! 113 | OSSpinLockLock(&self.lock) 114 | self.executingSignal = false 115 | if self.queuedValues.count != 0 { 116 | nextSignal = self.generator(self.value, self.queuedValues[0]) 117 | self.queuedValues.remove(at: 0) 118 | self.executingSignal = true 119 | } else { 120 | currentValue = self.value 121 | terminated = self.terminated 122 | } 123 | OSSpinLockUnlock(&self.lock) 124 | 125 | if terminated { 126 | self.subscriber.putNext(currentValue) 127 | self.subscriber.putCompletion() 128 | } else if nextSignal != nil { 129 | let disposable = nextSignal.start(next: { next in 130 | self.updateValue(next.0) 131 | switch next.1 { 132 | case let .Some(value): 133 | self.subscriber.putNext(value) 134 | case .None: 135 | break 136 | } 137 | }, error: { error in 138 | self.subscriber.putError(error) 139 | }, completed: { 140 | if leftFunction.swap(true) == true { 141 | self.headCompleted() 142 | } 143 | }) 144 | 145 | currentDisposable.set(disposable) 146 | } 147 | 148 | if leftFunction.swap(true) == false { 149 | break 150 | } 151 | } 152 | } 153 | 154 | func beginCompletion() { 155 | var executingSignal = false 156 | let currentValue: T 157 | OSSpinLockLock(&self.lock) 158 | executingSignal = self.executingSignal 159 | self.terminated = true 160 | currentValue = self.value 161 | OSSpinLockUnlock(&self.lock) 162 | 163 | if !executingSignal { 164 | self.subscriber.putNext(currentValue) 165 | self.subscriber.putCompletion() 166 | } 167 | } 168 | 169 | func dispose() { 170 | self.currentDisposable.dispose() 171 | self.disposable.dispose() 172 | } 173 | } 174 | 175 | public func reduceLeft(_ value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough), E>) -> (_ signal: Signal) -> Signal { 176 | return { signal in 177 | return Signal { subscriber in 178 | let state = ReduceQueueState(subscriber: subscriber, value: value, generator: generator) 179 | state.beginWithDisposable(signal.start(next: { next in 180 | state.enqueueNext(next) 181 | }, error: { error in 182 | subscriber.putError(error) 183 | }, completed: { 184 | state.beginCompletion() 185 | })) 186 | return state 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_SideEffects.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func beforeNext(_ f: @escaping(T) -> R) -> (Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | return signal.start(next: { next in 7 | let _ = f(next) 8 | subscriber.putNext(next) 9 | }, error: { error in 10 | subscriber.putError(error) 11 | }, completed: { 12 | subscriber.putCompletion() 13 | }) 14 | } 15 | } 16 | } 17 | 18 | public func afterNext(_ f: @escaping(T) -> R) -> (Signal) -> Signal { 19 | return { signal in 20 | return Signal { subscriber in 21 | return signal.start(next: { next in 22 | subscriber.putNext(next) 23 | let _ = f(next) 24 | }, error: { error in 25 | subscriber.putError(error) 26 | }, completed: { 27 | subscriber.putCompletion() 28 | }) 29 | } 30 | } 31 | } 32 | 33 | public func beforeStarted(_ f: @escaping() -> Void) -> (Signal) -> Signal { 34 | return { signal in 35 | return Signal { subscriber in 36 | f() 37 | return signal.start(next: { next in 38 | subscriber.putNext(next) 39 | }, error: { error in 40 | subscriber.putError(error) 41 | }, completed: { 42 | subscriber.putCompletion() 43 | }) 44 | } 45 | } 46 | } 47 | 48 | public func beforeCompleted(_ f: @escaping() -> Void) -> (Signal) -> Signal { 49 | return { signal in 50 | return Signal { subscriber in 51 | return signal.start(next: { next in 52 | subscriber.putNext(next) 53 | }, error: { error in 54 | subscriber.putError(error) 55 | }, completed: { 56 | f() 57 | subscriber.putCompletion() 58 | }) 59 | } 60 | } 61 | } 62 | 63 | public func afterCompleted(_ f: @escaping() -> Void) -> (Signal) -> Signal { 64 | return { signal in 65 | return Signal { subscriber in 66 | return signal.start(next: { next in 67 | subscriber.putNext(next) 68 | }, error: { error in 69 | subscriber.putError(error) 70 | }, completed: { 71 | subscriber.putCompletion() 72 | f() 73 | }) 74 | } 75 | } 76 | } 77 | 78 | public func afterDisposed(_ f: @escaping() -> R) -> (Signal) -> Signal { 79 | return { signal in 80 | return Signal { subscriber in 81 | let disposable = DisposableSet() 82 | disposable.add(signal.start(next: { next in 83 | subscriber.putNext(next) 84 | }, error: { error in 85 | subscriber.putError(error) 86 | }, completed: { 87 | subscriber.putCompletion() 88 | })) 89 | disposable.add(ActionDisposable { 90 | let _ = f() 91 | }) 92 | 93 | return disposable 94 | } 95 | } 96 | } 97 | 98 | public func withState(_ signal: Signal, _ initialState: @escaping() -> S, next: @escaping(T, S) -> Void = { _, _ in }, error: @escaping(E, S) -> Void = { _, _ in }, completed: @escaping(S) -> Void = { _ in }, disposed: @escaping(S) -> Void = { _ in }) -> Signal { 99 | return Signal { subscriber in 100 | let state = initialState() 101 | let disposable = signal.start(next: { vNext in 102 | next(vNext, state) 103 | subscriber.putNext(vNext) 104 | }, error: { vError in 105 | error(vError, state) 106 | subscriber.putError(vError) 107 | }, completed: { 108 | completed(state) 109 | subscriber.putCompletion() 110 | }) 111 | return ActionDisposable { 112 | disposable.dispose() 113 | disposed(state) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Single.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func single(_ value: T, _ errorType: E.Type) -> Signal { 4 | return Signal { subscriber in 5 | subscriber.putNext(value) 6 | subscriber.putCompletion() 7 | 8 | return EmptyDisposable 9 | } 10 | } 11 | 12 | public func fail(_ valueType: T.Type, _ error: E) -> Signal { 13 | return Signal { subscriber in 14 | subscriber.putError(error) 15 | 16 | return EmptyDisposable 17 | } 18 | } 19 | 20 | public func complete(_ valueType: T.Type, _ error: E.Type) -> Signal { 21 | return Signal { subscriber in 22 | subscriber.putCompletion() 23 | 24 | return EmptyDisposable 25 | } 26 | } 27 | 28 | public func never(_ valueType: T.Type, _ error: E.Type) -> Signal { 29 | return Signal { _ in 30 | return EmptyDisposable 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Take.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func take(_ count: Int) -> (Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | let counter = Atomic(value: 0) 7 | return signal.start(next: { next in 8 | var passthrough = false 9 | var complete = false 10 | let _ = counter.modify { value in 11 | let updatedCount = value + 1 12 | passthrough = updatedCount <= count 13 | complete = updatedCount == count 14 | return updatedCount 15 | } 16 | if passthrough { 17 | subscriber.putNext(next) 18 | } 19 | if complete { 20 | subscriber.putCompletion() 21 | } 22 | }, error: { error in 23 | subscriber.putError(error) 24 | }, completed: { 25 | subscriber.putCompletion() 26 | }) 27 | } 28 | } 29 | } 30 | 31 | public struct SignalTakeAction { 32 | public let passthrough: Bool 33 | public let complete: Bool 34 | 35 | public init(passthrough: Bool, complete: Bool) { 36 | self.passthrough = passthrough 37 | self.complete = complete 38 | } 39 | } 40 | 41 | public func take(until: @escaping (T) -> SignalTakeAction) -> (Signal) -> Signal { 42 | return { signal in 43 | return Signal { subscriber in 44 | return signal.start(next: { next in 45 | let action = until(next) 46 | if action.passthrough { 47 | subscriber.putNext(next) 48 | } 49 | if action.complete { 50 | subscriber.putCompletion() 51 | } 52 | }, error: { error in 53 | subscriber.putError(error) 54 | }, completed: { 55 | subscriber.putCompletion() 56 | }) 57 | } 58 | } 59 | } 60 | 61 | public func last(signal: Signal) -> Signal { 62 | return Signal { subscriber in 63 | let value = Atomic(value: nil) 64 | return signal.start(next: { next in 65 | let _ = value.swap(next) 66 | }, error: { error in 67 | subscriber.putError(error) 68 | }, completed: { 69 | subscriber.putNext(value.with({ $0 })) 70 | subscriber.putCompletion() 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftSignalKit/Signal_Timing.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func delay(_ timeout: Double, queue: Queue) -> (_ signal: Signal) -> Signal { 4 | return { signal in 5 | return Signal { subscriber in 6 | let disposable = MetaDisposable() 7 | queue.async { 8 | let timer = Timer(timeout: timeout, repeat: false, completion: { 9 | disposable.set(signal.start(next: { next in 10 | subscriber.putNext(next) 11 | }, error: { error in 12 | subscriber.putError(error) 13 | }, completed: { 14 | subscriber.putCompletion() 15 | })) 16 | }, queue: queue) 17 | 18 | disposable.set(ActionDisposable { 19 | queue.async { 20 | timer.invalidate() 21 | } 22 | }) 23 | 24 | timer.start() 25 | } 26 | return disposable 27 | } 28 | } 29 | } 30 | 31 | public func suspendAwareDelay(_ timeout: Double, granularity: Double = 4.0, queue: Queue) -> (_ signal: Signal) -> Signal { 32 | return { signal in 33 | return Signal { subscriber in 34 | let disposable = MetaDisposable() 35 | queue.async { 36 | let beginTimestamp = CFAbsoluteTimeGetCurrent() 37 | 38 | let startFinalTimer: () -> Void = { 39 | let finalTimeout = beginTimestamp + timeout - CFAbsoluteTimeGetCurrent() 40 | let timer = Timer(timeout: max(0.0, finalTimeout), repeat: false, completion: { 41 | disposable.set(signal.start(next: { next in 42 | subscriber.putNext(next) 43 | }, error: { error in 44 | subscriber.putError(error) 45 | }, completed: { 46 | subscriber.putCompletion() 47 | })) 48 | }, queue: queue) 49 | disposable.set(ActionDisposable { 50 | queue.async { 51 | timer.invalidate() 52 | } 53 | }) 54 | timer.start() 55 | } 56 | 57 | if timeout <= granularity * 1.1 { 58 | startFinalTimer() 59 | } else { 60 | var invalidateImpl: (() -> Void)? 61 | let timer = Timer(timeout: granularity, repeat: true, completion: { 62 | let currentTimestamp = CFAbsoluteTimeGetCurrent() 63 | if beginTimestamp + timeout - granularity * 1.1 <= currentTimestamp { 64 | invalidateImpl?() 65 | startFinalTimer() 66 | } 67 | }, queue: queue) 68 | 69 | invalidateImpl = { 70 | queue.async { 71 | timer.invalidate() 72 | } 73 | } 74 | 75 | disposable.set(ActionDisposable { 76 | invalidateImpl?() 77 | }) 78 | 79 | timer.start() 80 | } 81 | } 82 | return disposable 83 | } 84 | } 85 | } 86 | 87 | public func timeout(_ timeout: Double, queue: Queue, alternate: Signal) -> (Signal) -> Signal { 88 | return { signal in 89 | return Signal { subscriber in 90 | let disposable = MetaDisposable() 91 | let timer = Timer(timeout: timeout, repeat: false, completion: { 92 | disposable.set(alternate.start(next: { next in 93 | subscriber.putNext(next) 94 | }, error: { error in 95 | subscriber.putError(error) 96 | }, completed: { 97 | subscriber.putCompletion() 98 | })) 99 | }, queue: queue) 100 | 101 | disposable.set(signal.start(next: { next in 102 | timer.invalidate() 103 | subscriber.putNext(next) 104 | }, error: { error in 105 | timer.invalidate() 106 | subscriber.putError(error) 107 | }, completed: { 108 | timer.invalidate() 109 | subscriber.putCompletion() 110 | })) 111 | timer.start() 112 | 113 | let disposableSet = DisposableSet() 114 | disposableSet.add(ActionDisposable { 115 | timer.invalidate() 116 | }) 117 | disposableSet.add(disposable) 118 | 119 | return disposableSet 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /SwiftSignalKit/Subscriber.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Subscriber { 4 | private var next: ((T) -> Void)! 5 | private var error: ((E) -> Void)! 6 | private var completed: (() -> Void)! 7 | 8 | private var lock = pthread_mutex_t() 9 | private var terminated = false 10 | internal var disposable: Disposable! 11 | 12 | public init(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) { 13 | self.next = next 14 | self.error = error 15 | self.completed = completed 16 | pthread_mutex_init(&self.lock, nil) 17 | } 18 | 19 | deinit { 20 | var freeDisposable: Disposable? 21 | pthread_mutex_lock(&self.lock) 22 | if let disposable = self.disposable { 23 | freeDisposable = disposable 24 | self.disposable = nil 25 | } 26 | pthread_mutex_unlock(&self.lock) 27 | if let freeDisposableValue = freeDisposable { 28 | withExtendedLifetime(freeDisposableValue, { 29 | }) 30 | freeDisposable = nil 31 | } 32 | 33 | pthread_mutex_destroy(&self.lock) 34 | } 35 | 36 | internal func assignDisposable(_ disposable: Disposable) { 37 | var dispose = false 38 | pthread_mutex_lock(&self.lock) 39 | if self.terminated { 40 | dispose = true 41 | } else { 42 | self.disposable = disposable 43 | } 44 | pthread_mutex_unlock(&self.lock) 45 | 46 | if dispose { 47 | disposable.dispose() 48 | } 49 | } 50 | 51 | internal func markTerminatedWithoutDisposal() { 52 | pthread_mutex_lock(&self.lock) 53 | if !self.terminated { 54 | self.terminated = true 55 | self.next = nil 56 | self.error = nil 57 | self.completed = nil 58 | } 59 | pthread_mutex_unlock(&self.lock) 60 | } 61 | 62 | public func putNext(_ next: T) { 63 | var action: ((T) -> Void)! = nil 64 | pthread_mutex_lock(&self.lock) 65 | if !self.terminated { 66 | action = self.next 67 | } 68 | pthread_mutex_unlock(&self.lock) 69 | 70 | if action != nil { 71 | action(next) 72 | } 73 | } 74 | 75 | public func putError(_ error: E) { 76 | var action: ((E) -> Void)! = nil 77 | 78 | var disposeDisposable: Disposable? 79 | 80 | pthread_mutex_lock(&self.lock) 81 | if !self.terminated { 82 | action = self.error 83 | self.next = nil 84 | self.error = nil 85 | self.completed = nil; 86 | self.terminated = true 87 | disposeDisposable = self.disposable 88 | self.disposable = nil 89 | 90 | } 91 | pthread_mutex_unlock(&self.lock) 92 | 93 | if action != nil { 94 | action(error) 95 | } 96 | 97 | if let disposeDisposable = disposeDisposable { 98 | disposeDisposable.dispose() 99 | } 100 | } 101 | 102 | public func putCompletion() { 103 | var action: (() -> Void)! = nil 104 | 105 | var disposeDisposable: Disposable? = nil 106 | 107 | var next: ((T) -> Void)? 108 | var error: ((E) -> Void)? 109 | var completed: (() -> Void)? 110 | 111 | pthread_mutex_lock(&self.lock) 112 | if !self.terminated { 113 | action = self.completed 114 | next = self.next 115 | self.next = nil 116 | error = self.error 117 | self.error = nil 118 | completed = self.completed 119 | self.completed = nil 120 | self.terminated = true 121 | 122 | disposeDisposable = self.disposable 123 | self.disposable = nil 124 | } 125 | pthread_mutex_unlock(&self.lock) 126 | 127 | if let next = next { 128 | withExtendedLifetime(next, {}) 129 | } 130 | if let error = error { 131 | withExtendedLifetime(error, {}) 132 | } 133 | if let completed = completed { 134 | withExtendedLifetime(completed, {}) 135 | } 136 | 137 | if action != nil { 138 | action() 139 | } 140 | 141 | if let disposeDisposable = disposeDisposable { 142 | disposeDisposable.dispose() 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /SwiftSignalKit/SwiftSignalKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSignalKit.h 3 | // SwiftSignalKit 4 | // 5 | // Created by Peter on 10/06/15. 6 | // Copyright (c) 2015 Telegram. All rights reserved. 7 | // 8 | 9 | #if __IPHONE_OS_VERSION_MIN_REQUIRED 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | //! Project version number for SwiftSignalKit. 16 | FOUNDATION_EXPORT double SwiftSignalKitVersionNumber; 17 | 18 | //! Project version string for SwiftSignalKit. 19 | FOUNDATION_EXPORT const unsigned char SwiftSignalKitVersionString[]; 20 | 21 | // In this header, you should import all the public headers of your framework using statements like #import 22 | 23 | 24 | -------------------------------------------------------------------------------- /SwiftSignalKit/ThreadPool.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class ThreadPoolTaskState { 4 | public let cancelled = Atomic(value: false) 5 | } 6 | 7 | public final class ThreadPoolTask { 8 | private let state = ThreadPoolTaskState() 9 | private let action: (ThreadPoolTaskState) -> () 10 | 11 | public init(_ action: @escaping(ThreadPoolTaskState) -> ()) { 12 | self.action = action 13 | } 14 | 15 | func execute() { 16 | if !state.cancelled.with { $0 } { 17 | self.action(self.state) 18 | } 19 | } 20 | 21 | public func cancel() { 22 | let _ = self.state.cancelled.swap(true) 23 | } 24 | } 25 | 26 | public final class ThreadPoolQueue : Equatable { 27 | private weak var threadPool: ThreadPool? 28 | private var tasks: [ThreadPoolTask] = [] 29 | 30 | public init(threadPool: ThreadPool) { 31 | self.threadPool = threadPool 32 | } 33 | 34 | public func addTask(_ task: ThreadPoolTask) { 35 | if let threadPool = self.threadPool { 36 | threadPool.workOnQueue(self, action: { 37 | self.tasks.append(task) 38 | }) 39 | } 40 | } 41 | 42 | fileprivate func popFirstTask() -> ThreadPoolTask? { 43 | if self.tasks.count != 0 { 44 | let task = self.tasks[0]; 45 | self.tasks.remove(at: 0) 46 | return task 47 | } else { 48 | return nil 49 | } 50 | } 51 | 52 | fileprivate func hasTasks() -> Bool { 53 | return self.tasks.count != 0 54 | } 55 | } 56 | 57 | public func ==(lhs: ThreadPoolQueue, rhs: ThreadPoolQueue) -> Bool { 58 | return lhs === rhs 59 | } 60 | 61 | @objc public final class ThreadPool: NSObject { 62 | private var threads: [Thread] = [] 63 | private var queues: [ThreadPoolQueue] = [] 64 | private var takenQueues: [ThreadPoolQueue] = [] 65 | private var mutex: pthread_mutex_t 66 | private var condition: pthread_cond_t 67 | 68 | @objc class func threadEntryPoint(_ threadPool: ThreadPool) { 69 | var queue: ThreadPoolQueue! 70 | 71 | while (true) { 72 | var task: ThreadPoolTask! 73 | 74 | pthread_mutex_lock(&threadPool.mutex); 75 | 76 | if queue != nil { 77 | if let index = threadPool.takenQueues.index(of: queue) { 78 | threadPool.takenQueues.remove(at: index) 79 | } 80 | 81 | if queue.hasTasks() { 82 | threadPool.queues.append(queue); 83 | } 84 | } 85 | 86 | while (true) 87 | { 88 | while threadPool.queues.count == 0 { 89 | pthread_cond_wait(&threadPool.condition, &threadPool.mutex); 90 | } 91 | 92 | if threadPool.queues.count != 0 { 93 | queue = threadPool.queues[0] 94 | } 95 | 96 | if queue != nil { 97 | task = queue.popFirstTask() 98 | threadPool.takenQueues.append(queue) 99 | 100 | if let index = threadPool.queues.index(of: queue) { 101 | threadPool.queues.remove(at: index) 102 | } 103 | 104 | break 105 | } 106 | } 107 | pthread_mutex_unlock(&threadPool.mutex); 108 | 109 | if task != nil { 110 | autoreleasepool { 111 | task.execute() 112 | } 113 | } 114 | } 115 | } 116 | 117 | public init(threadCount: Int, threadPriority: Double) { 118 | assert(threadCount > 0, "threadCount < 0") 119 | 120 | self.mutex = pthread_mutex_t() 121 | self.condition = pthread_cond_t() 122 | pthread_mutex_init(&self.mutex, nil) 123 | pthread_cond_init(&self.condition, nil) 124 | 125 | super.init() 126 | 127 | for _ in 0 ..< threadCount { 128 | let thread = Thread(target: ThreadPool.self, selector: #selector(ThreadPool.threadEntryPoint(_:)), object: self) 129 | thread.threadPriority = threadPriority 130 | self.threads.append(thread) 131 | thread.start() 132 | } 133 | } 134 | 135 | deinit { 136 | pthread_mutex_destroy(&self.mutex) 137 | pthread_cond_destroy(&self.condition) 138 | } 139 | 140 | public func addTask(_ task: ThreadPoolTask) { 141 | let tempQueue = self.nextQueue() 142 | tempQueue.addTask(task) 143 | } 144 | 145 | fileprivate func workOnQueue(_ queue: ThreadPoolQueue, action: () -> ()) { 146 | pthread_mutex_lock(&self.mutex) 147 | action() 148 | if !self.queues.contains(queue) && !self.takenQueues.contains(queue) { 149 | self.queues.append(queue) 150 | } 151 | pthread_cond_broadcast(&self.condition) 152 | pthread_mutex_unlock(&self.mutex) 153 | } 154 | 155 | public func nextQueue() -> ThreadPoolQueue { 156 | return ThreadPoolQueue(threadPool: self) 157 | } 158 | 159 | public func isCurrentThreadInPool() -> Bool { 160 | let currentThread = Thread.current 161 | for thread in self.threads { 162 | if currentThread.isEqual(thread) { 163 | return true 164 | } 165 | } 166 | return false 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /SwiftSignalKit/Timer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class Timer { 4 | private let timer = Atomic(value: nil) 5 | private let timeout: Double 6 | private let `repeat`: Bool 7 | private let completion: () -> Void 8 | private let queue: Queue 9 | 10 | public init(timeout: Double, `repeat`: Bool, completion: @escaping() -> Void, queue: Queue) { 11 | self.timeout = timeout 12 | self.`repeat` = `repeat` 13 | self.completion = completion 14 | self.queue = queue 15 | } 16 | 17 | deinit { 18 | self.invalidate() 19 | } 20 | 21 | public func start() { 22 | let timer = DispatchSource.makeTimerSource(queue: self.queue.queue) 23 | timer.setEventHandler(handler: { [weak self] in 24 | if let strongSelf = self { 25 | strongSelf.completion() 26 | if !strongSelf.`repeat` { 27 | strongSelf.invalidate() 28 | } 29 | } 30 | }) 31 | let _ = self.timer.modify { _ in 32 | return timer 33 | } 34 | 35 | if self.`repeat` { 36 | let time: DispatchTime = DispatchTime.now() + self.timeout 37 | timer.scheduleRepeating(deadline: time, interval: self.timeout) 38 | } else { 39 | let time: DispatchTime = DispatchTime.now() + self.timeout 40 | timer.scheduleOneshot(deadline: time) 41 | } 42 | 43 | timer.resume() 44 | } 45 | 46 | public func invalidate() { 47 | let _ = self.timer.modify { timer in 48 | timer?.cancel() 49 | return nil 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SwiftSignalKit/ValuePipe.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class ValuePipe { 4 | private let subscribers = Atomic(value: Bag<(T) -> Void>()) 5 | 6 | public init() { 7 | } 8 | 9 | public func signal() -> Signal { 10 | return Signal { [weak self] subscriber in 11 | if let strongSelf = self { 12 | let index = strongSelf.subscribers.with { value -> Bag.Index in 13 | return value.add { next in 14 | subscriber.putNext(next) 15 | } 16 | } 17 | 18 | return ActionDisposable { [weak strongSelf] in 19 | if let strongSelf = strongSelf { 20 | strongSelf.subscribers.with { value -> Void in 21 | value.remove(index) 22 | } 23 | } 24 | } 25 | } else { 26 | return EmptyDisposable 27 | } 28 | } 29 | } 30 | 31 | public func putNext(_ next: T) { 32 | let items = self.subscribers.with { value -> [(T) -> Void] in 33 | return value.copyItems() 34 | } 35 | for f in items { 36 | f(next) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SwiftSignalKitMac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2016 Telegram. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftSignalKitMac/SwiftSignalKitMac.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSignalKitMac.h 3 | // SwiftSignalKitMac 4 | // 5 | // Created by Peter on 9/5/16. 6 | // Copyright © 2016 Telegram. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftSignalKitMac. 12 | FOUNDATION_EXPORT double SwiftSignalKitMacVersionNumber; 13 | 14 | //! Project version string for SwiftSignalKitMac. 15 | FOUNDATION_EXPORT const unsigned char SwiftSignalKitMacVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /SwiftSignalKitTests/DeallocatingObject.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | internal class DeallocatingObject : CustomStringConvertible { 4 | private var deallocated: UnsafeMutablePointer 5 | 6 | init(deallocated: UnsafeMutablePointer) { 7 | self.deallocated = deallocated 8 | } 9 | 10 | deinit { 11 | self.deallocated.pointee = true 12 | } 13 | 14 | var description: String { 15 | get { 16 | return "" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SwiftSignalKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftSignalKitTests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import SwiftSignalKit 4 | import Foundation 5 | 6 | final class DisposableLock { 7 | private var action: (() -> Void)? 8 | private var lock = pthread_mutex_t() 9 | 10 | init(action: @escaping () -> Void) { 11 | self.action = action 12 | pthread_mutex_init(&self.lock, nil) 13 | } 14 | 15 | func dispose() { 16 | var action: (() -> Void)? 17 | pthread_mutex_lock(&self.lock) 18 | action = self.action 19 | self.action = nil 20 | pthread_mutex_unlock(&self.lock) 21 | if let action = action { 22 | action() 23 | } 24 | } 25 | } 26 | 27 | final class DisposableSpinLock { 28 | private var action: (() -> Void)? 29 | private var lock = OSSpinLock() 30 | 31 | init(action: @escaping () -> Void) { 32 | self.action = action 33 | } 34 | 35 | func dispose() { 36 | var action: (() -> Void)? 37 | OSSpinLockLock(&self.lock) 38 | action = self.action 39 | self.action = nil 40 | OSSpinLockUnlock(&self.lock) 41 | if let action = action { 42 | action() 43 | } 44 | } 45 | } 46 | 47 | final class DisposableNoLock { 48 | private var action: (() -> Void)? 49 | 50 | init(action: @escaping () -> Void) { 51 | self.action = action 52 | } 53 | 54 | func dispose() { 55 | var action: (() -> Void)? 56 | action = self.action 57 | self.action = nil 58 | if let action = action { 59 | action() 60 | } 61 | } 62 | } 63 | 64 | final class DisposableAtomic { 65 | private var action: () -> Void 66 | private var disposed: Int32 = 0 67 | 68 | init(action: @escaping () -> Void) { 69 | self.action = action 70 | } 71 | 72 | func dispose() { 73 | if OSAtomicCompareAndSwap32(0, 1, &self.disposed) { 74 | self.action() 75 | } 76 | } 77 | } 78 | 79 | class PerformanceTests: XCTestCase { 80 | override func setUp() { 81 | super.setUp() 82 | } 83 | 84 | override func tearDown() { 85 | super.tearDown() 86 | } 87 | 88 | func testMeasureLock() { 89 | measure { 90 | for _ in 0 ..< 1000000 { 91 | let disposable = DisposableLock(action: {}) 92 | disposable.dispose() 93 | } 94 | } 95 | } 96 | 97 | func testMeasureSpinlock() { 98 | measure { 99 | for _ in 0 ..< 1000000 { 100 | let disposable = DisposableSpinLock(action: {}) 101 | disposable.dispose() 102 | } 103 | } 104 | } 105 | 106 | func testMeasureAtomic() { 107 | measure { 108 | for _ in 0 ..< 1000000 { 109 | let disposable = DisposableAtomic(action: {}) 110 | disposable.dispose() 111 | } 112 | } 113 | } 114 | 115 | func read(_ idxin: Int, _ size: Int, _ tree: inout [Int: Int], _ reads: inout Set) -> Int { 116 | var idx = idxin 117 | var sum = 0 118 | while idx <= size { 119 | print("read at \(idx)") 120 | if let value = tree[idx] { 121 | sum += value 122 | } 123 | reads.insert(idx) 124 | idx += (idx & -idx) 125 | } 126 | return sum 127 | } 128 | 129 | func update(_ idxin: Int, _ val: Int, _ tree: inout [Int: Int], _ updates: inout Set) { 130 | var idx = idxin 131 | while (idx > 0) { 132 | if let value = tree[idx] { 133 | tree[idx] = value + val 134 | } else { 135 | tree[idx] = val 136 | } 137 | //print("write at \(idx)") 138 | updates.insert(idx) 139 | idx -= (idx & -idx) 140 | } 141 | } 142 | 143 | func testTree() { 144 | let size = 2_000_000 145 | var dict: [Int: Int] = [:] 146 | 147 | var updates = Set() 148 | var index = 0 149 | for _ in 1 ..< 100_000 { 150 | //update(Int(1 + arc4random_uniform(UInt32(size))), 1, &dict, &updates) 151 | update(index, 1, &dict, &updates) 152 | index += Int(1 + arc4random_uniform(100)) 153 | } 154 | update(size - 1, 1, &dict, &updates) 155 | print("update ops = \(updates.count), tree = \(dict.count) items") 156 | 157 | var reads = Set() 158 | let sum = read(1, size, &dict, &reads) 159 | print("read = \(sum) ops = \(reads.count)") 160 | 161 | update(99, -2, &dict, &updates) 162 | reads.removeAll() 163 | let sum2 = read(1, size, &dict, &reads) 164 | print("read2 = \(sum2) ops = \(reads.count)") 165 | } 166 | } 167 | --------------------------------------------------------------------------------