Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface SAtomic : NSObject
- (instancetype)initWithValue:(id)value;
- (instancetype)initWithValue:(id)value recursive:(bool)recursive;
- (id)swap:(id)newValue;
- (id)value;
- (id)modify:(id (^)(id))f;
- (id)with:(id (^)(id))f;
@end
@@ -0,0 +1,93 @@
#import "SAtomic.h"
#import <pthread.h>
@interface SAtomic ()
{
pthread_mutex_t _lock;
pthread_mutexattr_t _attr;
bool _isRecursive;
id _value;
}
@end
@implementation SAtomic
- (instancetype)initWithValue:(id)value
{
self = [super init];
if (self != nil)
{
pthread_mutex_init(&_lock, NULL);
_value = value;
}
return self;
}
- (instancetype)initWithValue:(id)value recursive:(bool)recursive {
self = [super init];
if (self != nil)
{
_isRecursive = recursive;
if (recursive) {
pthread_mutexattr_init(&_attr);
pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_lock, &_attr);
} else {
pthread_mutex_init(&_lock, NULL);
}
_value = value;
}
return self;
}
- (void)dealloc {
if (_isRecursive) {
pthread_mutexattr_destroy(&_attr);
}
pthread_mutex_destroy(&_lock);
}
- (id)swap:(id)newValue
{
id previousValue = nil;
pthread_mutex_lock(&_lock);
previousValue = _value;
_value = newValue;
pthread_mutex_unlock(&_lock);
return previousValue;
}
- (id)value
{
id previousValue = nil;
pthread_mutex_lock(&_lock);
previousValue = _value;
pthread_mutex_unlock(&_lock);
return previousValue;
}
- (id)modify:(id (^)(id))f
{
id newValue = nil;
pthread_mutex_lock(&_lock);
newValue = f(_value);
_value = newValue;
pthread_mutex_unlock(&_lock);
return newValue;
}
- (id)with:(id (^)(id))f
{
id result = nil;
pthread_mutex_lock(&_lock);
result = f(_value);
pthread_mutex_unlock(&_lock);
return result;
}
@end
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface SBag : NSObject
- (NSInteger)addItem:(id)item;
- (void)enumerateItems:(void (^)(id))block;
- (void)removeItem:(NSInteger)key;
- (bool)isEmpty;
- (NSArray *)copyItems;
@end
@@ -0,0 +1,74 @@
#import "SBag.h"
@interface SBag ()
{
NSInteger _nextKey;
NSMutableArray *_items;
NSMutableArray *_itemKeys;
}
@end
@implementation SBag
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_items = [[NSMutableArray alloc] init];
_itemKeys = [[NSMutableArray alloc] init];
}
return self;
}
- (NSInteger)addItem:(id)item
{
if (item == nil)
return -1;
NSInteger key = _nextKey;
[_items addObject:item];
[_itemKeys addObject:@(key)];
_nextKey++;
return key;
}
- (void)enumerateItems:(void (^)(id))block
{
if (block)
{
for (id item in _items)
{
block(item);
}
}
}
- (void)removeItem:(NSInteger)key
{
NSUInteger index = 0;
for (NSNumber *itemKey in _itemKeys)
{
if ([itemKey integerValue] == key)
{
[_items removeObjectAtIndex:index];
[_itemKeys removeObjectAtIndex:index];
break;
}
index++;
}
}
- (bool)isEmpty
{
return _items.count == 0;
}
- (NSArray *)copyItems
{
return [[NSArray alloc] initWithArray:_items];
}
@end
@@ -0,0 +1,7 @@
#import <SSignalKit/SDisposable.h>
@interface SBlockDisposable : NSObject <SDisposable>
- (instancetype)initWithBlock:(void (^)())block;
@end
@@ -0,0 +1,58 @@
#import "SBlockDisposable.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
@interface SBlockDisposable ()
{
void *_block;
}
@end
@implementation SBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_block = (__bridge_retained void *)[block copy];
}
return self;
}
- (void)dealloc
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
strongBlock = nil;
}
}
}
}
- (void)dispose
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
((dispatch_block_t)strongBlock)();
strongBlock = nil;
}
}
}
}
@end
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
@protocol SDisposable <NSObject>
- (void)dispose;
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SDisposable.h>
@class SSignal;
@interface SDisposableSet : NSObject <SDisposable>
- (void)add:(id<SDisposable>)disposable;
- (void)remove:(id<SDisposable>)disposable;
@end
@@ -0,0 +1,95 @@
#import "SDisposableSet.h"
#import "SSignal.h"
#import <libkern/OSAtomic.h>
@interface SDisposableSet ()
{
OSSpinLock _lock;
bool _disposed;
id<SDisposable> _singleDisposable;
NSArray *_multipleDisposables;
}
@end
@implementation SDisposableSet
- (void)add:(id<SDisposable>)disposable
{
if (disposable == nil)
return;
bool dispose = false;
OSSpinLockLock(&_lock);
dispose = _disposed;
if (!dispose)
{
if (_multipleDisposables != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
[multipleDisposables addObject:disposable];
_multipleDisposables = multipleDisposables;
}
else if (_singleDisposable != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithObjects:_singleDisposable, disposable, nil];
_multipleDisposables = multipleDisposables;
_singleDisposable = nil;
}
else
{
_singleDisposable = disposable;
}
}
OSSpinLockUnlock(&_lock);
if (dispose)
[disposable dispose];
}
- (void)remove:(id<SDisposable>)disposable {
OSSpinLockLock(&_lock);
if (_multipleDisposables != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
[multipleDisposables removeObject:disposable];
_multipleDisposables = multipleDisposables;
}
else if (_singleDisposable == disposable)
{
_singleDisposable = nil;
}
OSSpinLockUnlock(&_lock);
}
- (void)dispose
{
id<SDisposable> singleDisposable = nil;
NSArray *multipleDisposables = nil;
OSSpinLockLock(&_lock);
if (!_disposed)
{
_disposed = true;
singleDisposable = _singleDisposable;
multipleDisposables = _multipleDisposables;
_singleDisposable = nil;
_multipleDisposables = nil;
}
OSSpinLockUnlock(&_lock);
if (singleDisposable != nil)
[singleDisposable dispose];
if (multipleDisposables != nil)
{
for (id<SDisposable> disposable in multipleDisposables)
{
[disposable dispose];
}
}
}
@end
@@ -0,0 +1,7 @@
#import <SSignalKit/SDisposable.h>
@interface SMetaDisposable : NSObject <SDisposable>
- (void)setDisposable:(id<SDisposable>)disposable;
@end
@@ -0,0 +1,53 @@
#import "SMetaDisposable.h"
#import <libkern/OSAtomic.h>
@interface SMetaDisposable ()
{
OSSpinLock _lock;
bool _disposed;
id<SDisposable> _disposable;
}
@end
@implementation SMetaDisposable
- (void)setDisposable:(id<SDisposable>)disposable
{
id<SDisposable> previousDisposable = nil;
bool dispose = false;
OSSpinLockLock(&_lock);
dispose = _disposed;
if (!dispose)
{
previousDisposable = _disposable;
_disposable = disposable;
}
OSSpinLockUnlock(&_lock);
if (previousDisposable != nil)
[previousDisposable dispose];
if (dispose)
[disposable dispose];
}
- (void)dispose
{
id<SDisposable> disposable = nil;
OSSpinLockLock(&_lock);
if (!_disposed)
{
disposable = _disposable;
_disposed = true;
}
OSSpinLockUnlock(&_lock);
if (disposable != nil)
[disposable dispose];
}
@end
@@ -0,0 +1,11 @@
#import <SSignalKit/SSignal.h>
@interface SMulticastSignalManager : NSObject
- (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer;
- (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer;
- (SSignal *)multicastedPipeForKey:(NSString *)key;
- (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key;
@end
@@ -0,0 +1,171 @@
#import "SMulticastSignalManager.h"
#import "SSignal+Multicast.h"
#import "SSignal+SideEffects.h"
#import "SBag.h"
#import "SMetaDisposable.h"
#import "SBlockDisposable.h"
#import <libkern/OSAtomic.h>
@interface SMulticastSignalManager ()
{
OSSpinLock _lock;
NSMutableDictionary *_multicastSignals;
NSMutableDictionary *_standaloneSignalDisposables;
NSMutableDictionary *_pipeListeners;
}
@end
@implementation SMulticastSignalManager
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_multicastSignals = [[NSMutableDictionary alloc] init];
_standaloneSignalDisposables = [[NSMutableDictionary alloc] init];
_pipeListeners = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc
{
NSArray *disposables = nil;
OSSpinLockLock(&_lock);
disposables = [_standaloneSignalDisposables allValues];
OSSpinLockUnlock(&_lock);
for (id<SDisposable> disposable in disposables)
{
[disposable dispose];
}
}
- (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer
{
if (key == nil)
{
if (producer)
return producer();
else
return nil;
}
SSignal *signal = nil;
OSSpinLockLock(&_lock);
signal = _multicastSignals[key];
if (signal == nil)
{
__weak SMulticastSignalManager *weakSelf = self;
if (producer)
signal = producer();
if (signal != nil)
{
signal = [[signal onDispose:^
{
__strong SMulticastSignalManager *strongSelf = weakSelf;
if (strongSelf != nil)
{
OSSpinLockLock(&strongSelf->_lock);
[strongSelf->_multicastSignals removeObjectForKey:key];
OSSpinLockUnlock(&strongSelf->_lock);
}
}] multicast];
_multicastSignals[key] = signal;
}
}
OSSpinLockUnlock(&_lock);
return signal;
}
- (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer
{
if (key == nil)
return;
bool produce = false;
OSSpinLockLock(&_lock);
if (_standaloneSignalDisposables[key] == nil)
{
_standaloneSignalDisposables[key] = [[SMetaDisposable alloc] init];
produce = true;
}
OSSpinLockUnlock(&_lock);
if (produce)
{
__weak SMulticastSignalManager *weakSelf = self;
id<SDisposable> disposable = [producer() startWithNext:nil error:^(__unused id error)
{
__strong SMulticastSignalManager *strongSelf = weakSelf;
if (strongSelf != nil)
{
OSSpinLockLock(&strongSelf->_lock);
[strongSelf->_standaloneSignalDisposables removeObjectForKey:key];
OSSpinLockUnlock(&strongSelf->_lock);
}
} completed:^
{
__strong SMulticastSignalManager *strongSelf = weakSelf;
if (strongSelf != nil)
{
OSSpinLockLock(&strongSelf->_lock);
[strongSelf->_standaloneSignalDisposables removeObjectForKey:key];
OSSpinLockUnlock(&strongSelf->_lock);
}
}];
OSSpinLockLock(&_lock);
[(SMetaDisposable *)_standaloneSignalDisposables[key] setDisposable:disposable];
OSSpinLockUnlock(&_lock);
}
}
- (SSignal *)multicastedPipeForKey:(NSString *)key
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
OSSpinLockLock(&_lock);
SBag *bag = _pipeListeners[key];
if (bag == nil)
{
bag = [[SBag alloc] init];
_pipeListeners[key] = bag;
}
NSInteger index = [bag addItem:[^(id next)
{
[subscriber putNext:next];
} copy]];
OSSpinLockUnlock(&_lock);
return [[SBlockDisposable alloc] initWithBlock:^
{
OSSpinLockLock(&_lock);
SBag *bag = _pipeListeners[key];
[bag removeItem:index];
if ([bag isEmpty]) {
[_pipeListeners removeObjectForKey:key];
}
OSSpinLockUnlock(&_lock);
}];
}];
}
- (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key
{
OSSpinLockLock(&_lock);
NSArray *pipeListeners = [(SBag *)_pipeListeners[key] copyItems];
OSSpinLockUnlock(&_lock);
for (void (^listener)(id) in pipeListeners)
{
listener(next);
}
}
@end
@@ -0,0 +1,19 @@
#import <Foundation/Foundation.h>
@interface SQueue : NSObject
+ (SQueue *)mainQueue;
+ (SQueue *)concurrentDefaultQueue;
+ (SQueue *)concurrentBackgroundQueue;
+ (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue;
- (void)dispatch:(dispatch_block_t)block;
- (void)dispatchSync:(dispatch_block_t)block;
- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous;
- (dispatch_queue_t)_dispatch_queue;
- (bool)isCurrentQueue;
@end
@@ -0,0 +1,124 @@
#import "SQueue.h"
static const void *SQueueSpecificKey = &SQueueSpecificKey;
@interface SQueue ()
{
dispatch_queue_t _queue;
void *_queueSpecific;
bool _specialIsMainQueue;
}
@end
@implementation SQueue
+ (SQueue *)mainQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_main_queue() queueSpecific:NULL];
queue->_specialIsMainQueue = true;
});
return queue;
}
+ (SQueue *)concurrentDefaultQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) queueSpecific:NULL];
});
return queue;
}
+ (SQueue *)concurrentBackgroundQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) queueSpecific:NULL];
});
return queue;
}
+ (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue
{
return [[SQueue alloc] initWithNativeQueue:nativeQueue queueSpecific:NULL];
}
- (instancetype)init
{
dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
dispatch_queue_set_specific(queue, SQueueSpecificKey, (__bridge void *)self, NULL);
return [self initWithNativeQueue:queue queueSpecific:(__bridge void *)self];
}
- (instancetype)initWithNativeQueue:(dispatch_queue_t)queue queueSpecific:(void *)queueSpecific
{
self = [super init];
if (self != nil)
{
_queue = queue;
_queueSpecific = queueSpecific;
}
return self;
}
- (dispatch_queue_t)_dispatch_queue
{
return _queue;
}
- (void)dispatch:(dispatch_block_t)block
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else
dispatch_async(_queue, block);
}
- (void)dispatchSync:(dispatch_block_t)block
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else
dispatch_sync(_queue, block);
}
- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous {
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else {
if (synchronous) {
dispatch_sync(_queue, block);
} else {
dispatch_async(_queue, block);
}
}
}
- (bool)isCurrentQueue
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
return true;
else if (_specialIsMainQueue && [NSThread isMainThread])
return true;
return false;
}
@end
@@ -0,0 +1,8 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Accumulate)
- (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f;
- (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f;
@end
@@ -0,0 +1,52 @@
#import "SSignal+Accumulate.h"
@implementation SSignal (Accumulate)
- (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
__block id intermediateResult = value;
return [self startWithNext:^(id next)
{
intermediateResult = f(intermediateResult, next);
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
if (intermediateResult != nil)
[subscriber putNext:intermediateResult];
[subscriber putCompletion];
}];
}];
}
- (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
__block id intermediateResult = value;
void (^emit)(id) = ^(id next)
{
[subscriber putNext:next];
};
return [self startWithNext:^(id next)
{
intermediateResult = f(intermediateResult, next, emit);
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
if (intermediateResult != nil)
[subscriber putNext:intermediateResult];
[subscriber putCompletion];
}];
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Catch)
- (SSignal *)catch:(SSignal *(^)(id error))f;
- (SSignal *)restart;
- (SSignal *)retryIf:(bool (^)(id error))predicate;
@end
@@ -0,0 +1,147 @@
#import "SSignal+Catch.h"
#import "SMetaDisposable.h"
#import "SDisposableSet.h"
#import "SBlockDisposable.h"
#import "SAtomic.h"
@implementation SSignal (Catch)
- (SSignal *)catch:(SSignal *(^)(id error))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SDisposableSet *disposable = [[SDisposableSet alloc] init];
[disposable add:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
SSignal *signal = f(error);
[disposable add:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} completed:^
{
[subscriber putCompletion];
}]];
return disposable;
}];
}
static dispatch_block_t recursiveBlock(void (^block)(dispatch_block_t recurse))
{
return ^
{
block(recursiveBlock(block));
};
}
- (SSignal *)restart
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
{
NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
{
return current;
}];
if ([currentShouldRestart boolValue])
{
id<SDisposable> disposable = [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
recurse();
}];
[currentDisposable setDisposable:disposable];
}
});
start();
return [[SBlockDisposable alloc] initWithBlock:^
{
[currentDisposable dispose];
[shouldRestart modify:^id(__unused id current)
{
return @false;
}];
}];
}];
}
- (SSignal *)retryIf:(bool (^)(id error))predicate {
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
{
NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
{
return current;
}];
if ([currentShouldRestart boolValue])
{
id<SDisposable> disposable = [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
if (predicate(error)) {
recurse();
} else {
[subscriber putError:error];
}
} completed:^
{
[shouldRestart modify:^id(__unused id current) {
return @false;
}];
[subscriber putCompletion];
}];
[currentDisposable setDisposable:disposable];
} else {
[subscriber putCompletion];
}
});
start();
return [[SBlockDisposable alloc] initWithBlock:^
{
[currentDisposable dispose];
[shouldRestart modify:^id(__unused id current)
{
return @false;
}];
}];
}];
}
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Combine)
+ (SSignal *)combineSignals:(NSArray *)signals;
+ (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates;
+ (SSignal *)mergeSignals:(NSArray *)signals;
@end
@@ -0,0 +1,177 @@
#import "SSignal+Combine.h"
#import "SAtomic.h"
#import "SDisposableSet.h"
#import "SSignal+Single.h"
@interface SSignalCombineState : NSObject
@property (nonatomic, strong, readonly) NSDictionary *latestValues;
@property (nonatomic, strong, readonly) NSArray *completedStatuses;
@property (nonatomic) bool error;
@end
@implementation SSignalCombineState
- (instancetype)initWithLatestValues:(NSDictionary *)latestValues completedStatuses:(NSArray *)completedStatuses error:(bool)error
{
self = [super init];
if (self != nil)
{
_latestValues = latestValues;
_completedStatuses = completedStatuses;
_error = error;
}
return self;
}
@end
@implementation SSignal (Combine)
+ (SSignal *)combineSignals:(NSArray *)signals
{
if (signals.count == 0)
return [SSignal single:@[]];
else
return [self combineSignals:signals withInitialStates:nil];
}
+ (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
NSMutableArray *completedStatuses = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < signals.count; i++)
{
[completedStatuses addObject:@false];
}
NSMutableDictionary *initialLatestValues = [[NSMutableDictionary alloc] init];
for (NSUInteger i = 0; i < initialStates.count; i++)
{
initialLatestValues[@(i)] = initialStates[i];
}
SAtomic *combineState = [[SAtomic alloc] initWithValue:[[SSignalCombineState alloc] initWithLatestValues:initialLatestValues completedStatuses:completedStatuses error:false]];
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
NSUInteger index = 0;
NSUInteger count = signals.count;
for (SSignal *signal in signals)
{
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
SSignalCombineState *currentState = [combineState modify:^id(SSignalCombineState *state)
{
NSMutableDictionary *latestValues = [[NSMutableDictionary alloc] initWithDictionary:state.latestValues];
latestValues[@(index)] = next;
return [[SSignalCombineState alloc] initWithLatestValues:latestValues completedStatuses:state.completedStatuses error:state.error];
}];
NSMutableArray *latestValues = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < count; i++)
{
id value = currentState.latestValues[@(i)];
if (value == nil)
{
latestValues = nil;
break;
}
latestValues[i] = value;
}
if (latestValues != nil)
[subscriber putNext:latestValues];
}
error:^(id error)
{
__block bool hadError = false;
[combineState modify:^id(SSignalCombineState *state)
{
hadError = state.error;
return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:state.completedStatuses error:true];
}];
if (!hadError)
[subscriber putError:error];
} completed:^
{
__block bool wasCompleted = false;
__block bool isCompleted = false;
[combineState modify:^id(SSignalCombineState *state)
{
NSMutableArray *completedStatuses = [[NSMutableArray alloc] initWithArray:state.completedStatuses];
bool everyStatusWasCompleted = true;
for (NSNumber *nStatus in completedStatuses)
{
if (![nStatus boolValue])
{
everyStatusWasCompleted = false;
break;
}
}
completedStatuses[index] = @true;
bool everyStatusIsCompleted = true;
for (NSNumber *nStatus in completedStatuses)
{
if (![nStatus boolValue])
{
everyStatusIsCompleted = false;
break;
}
}
wasCompleted = everyStatusWasCompleted;
isCompleted = everyStatusIsCompleted;
return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:completedStatuses error:state.error];
}];
if (!wasCompleted && isCompleted)
[subscriber putCompletion];
}];
[compositeDisposable add:disposable];
index++;
}
return compositeDisposable;
}];
}
+ (SSignal *)mergeSignals:(NSArray *)signals
{
if (signals.count == 0)
return [SSignal complete];
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SDisposableSet *disposables = [[SDisposableSet alloc] init];
SAtomic *completedStates = [[SAtomic alloc] initWithValue:[[NSSet alloc] init]];
NSInteger index = -1;
NSUInteger count = signals.count;
for (SSignal *signal in signals)
{
index++;
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
NSSet *set = [completedStates modify:^id(NSSet *set)
{
return [set setByAddingObject:@(index)];
}];
if (set.count == count)
[subscriber putCompletion];
}];
[disposables add:disposable];
}
return disposables;
}];
}
@end
@@ -0,0 +1,14 @@
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SQueue.h>
#import <SSignalKit/SThreadPool.h>
@interface SSignal (Dispatch)
- (SSignal *)deliverOn:(SQueue *)queue;
- (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool;
- (SSignal *)startOn:(SQueue *)queue;
- (SSignal *)startOnThreadPool:(SThreadPool *)threadPool;
- (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay;
@end
@@ -0,0 +1,212 @@
#import "SSignal+Dispatch.h"
#import "SAtomic.h"
#import "STimer.h"
#import "SBlockDisposable.h"
#import "SMetaDisposable.h"
@interface SSignal_ThrottleContainer : NSObject
@property (nonatomic, strong, readonly) id value;
@property (nonatomic, readonly) bool committed;
@property (nonatomic, readonly) bool last;
@end
@implementation SSignal_ThrottleContainer
- (instancetype)initWithValue:(id)value committed:(bool)committed last:(bool)last {
self = [super init];
if (self != nil) {
_value = value;
_committed = committed;
_last = last;
}
return self;
}
@end
@implementation SSignal (Dispatch)
- (SSignal *)deliverOn:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[queue dispatch:^
{
[subscriber putNext:next];
}];
} error:^(id error)
{
[queue dispatch:^
{
[subscriber putError:error];
}];
} completed:^
{
[queue dispatch:^
{
[subscriber putCompletion];
}];
}];
}];
}
- (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SThreadPoolQueue *queue = [threadPool nextQueue];
return [self startWithNext:^(id next)
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putNext:next];
}];
[queue addTask:task];
} error:^(id error)
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putError:error];
}];
[queue addTask:task];
} completed:^
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putCompletion];
}];
[queue addTask:task];
}];
}];
}
- (SSignal *)startOn:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
__block bool isCancelled = false;
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
[disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
isCancelled = true;
}]];
[queue dispatch:^
{
if (!isCancelled)
{
[disposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}
}];
return disposable;
}];
}
- (SSignal *)startOnThreadPool:(SThreadPool *)threadPool
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (cancelled && cancelled())
return;
[disposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}];
[disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
[task cancel];
}]];
[threadPool addTask:task];
return disposable;
}];
}
- (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SAtomic *value = [[SAtomic alloc] initWithValue:nil];
STimer *timer = [[STimer alloc] initWithTimeout:delay repeat:false completion:^{
[value modify:^id(SSignal_ThrottleContainer *container) {
if (container != nil) {
if (!container.committed) {
[subscriber putNext:container.value];
container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:true last:container.last];
}
if (container.last) {
[subscriber putCompletion];
}
}
return container;
}];
} queue:queue];
return [[self deliverOn:queue] startWithNext:^(id next) {
[value modify:^id(SSignal_ThrottleContainer *container) {
if (container == nil) {
container = [[SSignal_ThrottleContainer alloc] initWithValue:next committed:false last:false];
}
return container;
}];
[timer invalidate];
[timer start];
} error:^(id error) {
[timer invalidate];
[subscriber putError:error];
} completed:^{
[timer invalidate];
__block bool start = false;
[value modify:^id(SSignal_ThrottleContainer *container) {
bool wasCommitted = false;
if (container == nil) {
wasCommitted = true;
container = [[SSignal_ThrottleContainer alloc] initWithValue:nil committed:true last:true];
} else {
wasCommitted = container.committed;
container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:container.committed last:true];
}
start = wasCommitted;
return container;
}];
if (start) {
[timer start];
} else {
[timer fireAndInvalidate];
}
}];
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Mapping)
- (SSignal *)map:(id (^)(id))f;
- (SSignal *)filter:(bool (^)(id))f;
- (SSignal *)ignoreRepeated;
@end
@@ -0,0 +1,83 @@
#import "SSignal+Mapping.h"
#import "SAtomic.h"
@interface SSignalIgnoreRepeatedState: NSObject
@property (nonatomic, strong) id value;
@property (nonatomic) bool hasValue;
@end
@implementation SSignalIgnoreRepeatedState
@end
@implementation SSignal (Mapping)
- (SSignal *)map:(id (^)(id))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:f(next)];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)filter:(bool (^)(id))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
if (f(next))
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)ignoreRepeated {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SAtomic *state = [[SAtomic alloc] initWithValue:[[SSignalIgnoreRepeatedState alloc] init]];
return [self startWithNext:^(id next) {
bool shouldPassthrough = [[state with:^id(SSignalIgnoreRepeatedState *state) {
if (!state.hasValue) {
state.hasValue = true;
state.value = next;
return @true;
} else if ((state.value == nil && next == nil) || [(id<NSObject>)state.value isEqual:next]) {
return @false;
}
state.value = next;
return @true;
}] boolValue];
if (shouldPassthrough) {
[subscriber putNext:next];
}
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
@end
@@ -0,0 +1,22 @@
#import <SSignalKit/SSignal.h>
@class SQueue;
@interface SSignal (Meta)
- (SSignal *)switchToLatest;
- (SSignal *)mapToSignal:(SSignal *(^)(id))f;
- (SSignal *)mapToQueue:(SSignal *(^)(id))f;
- (SSignal *)mapToThrottled:(SSignal *(^)(id))f;
- (SSignal *)then:(SSignal *)signal;
- (SSignal *)queue;
- (SSignal *)throttled;
+ (SSignal *)defer:(SSignal *(^)())generator;
@end
@interface SSignalQueue : NSObject
- (SSignal *)enqueue:(SSignal *)signal;
@end
@@ -0,0 +1,325 @@
#import "SSignal+Meta.h"
#import "SDisposableSet.h"
#import "SMetaDisposable.h"
#import "SSignal+Mapping.h"
#import "SAtomic.h"
#import "SSignal+Pipe.h"
#import <libkern/OSAtomic.h>
@interface SSignalQueueState : NSObject <SDisposable>
{
OSSpinLock _lock;
bool _executingSignal;
bool _terminated;
id<SDisposable> _disposable;
SMetaDisposable *_currentDisposable;
SSubscriber *_subscriber;
NSMutableArray *_queuedSignals;
bool _queueMode;
bool _throttleMode;
}
@end
@implementation SSignalQueueState
- (instancetype)initWithSubscriber:(SSubscriber *)subscriber queueMode:(bool)queueMode throttleMode:(bool)throttleMode
{
self = [super init];
if (self != nil)
{
_subscriber = subscriber;
_currentDisposable = [[SMetaDisposable alloc] init];
_queuedSignals = queueMode ? [[NSMutableArray alloc] init] : nil;
_queueMode = queueMode;
_throttleMode = throttleMode;
}
return self;
}
- (void)beginWithDisposable:(id<SDisposable>)disposable
{
_disposable = disposable;
}
- (void)enqueueSignal:(SSignal *)signal
{
bool startSignal = false;
OSSpinLockLock(&_lock);
if (_queueMode && _executingSignal) {
if (_throttleMode) {
[_queuedSignals removeAllObjects];
}
[_queuedSignals addObject:signal];
}
else
{
_executingSignal = true;
startSignal = true;
}
OSSpinLockUnlock(&_lock);
if (startSignal)
{
__weak SSignalQueueState *weakSelf = self;
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
[_subscriber putNext:next];
} error:^(id error)
{
[_subscriber putError:error];
} completed:^
{
__strong SSignalQueueState *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf headCompleted];
}
}];
[_currentDisposable setDisposable:disposable];
}
}
- (void)headCompleted
{
SSignal *nextSignal = nil;
bool terminated = false;
OSSpinLockLock(&_lock);
_executingSignal = false;
if (_queueMode)
{
if (_queuedSignals.count != 0)
{
nextSignal = _queuedSignals[0];
[_queuedSignals removeObjectAtIndex:0];
_executingSignal = true;
}
else
terminated = _terminated;
}
else
terminated = _terminated;
OSSpinLockUnlock(&_lock);
if (terminated)
[_subscriber putCompletion];
else if (nextSignal != nil)
{
__weak SSignalQueueState *weakSelf = self;
id<SDisposable> disposable = [nextSignal startWithNext:^(id next)
{
[_subscriber putNext:next];
} error:^(id error)
{
[_subscriber putError:error];
} completed:^
{
__strong SSignalQueueState *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf headCompleted];
}
}];
[_currentDisposable setDisposable:disposable];
}
}
- (void)beginCompletion
{
bool executingSignal = false;
OSSpinLockLock(&_lock);
executingSignal = _executingSignal;
_terminated = true;
OSSpinLockUnlock(&_lock);
if (!executingSignal)
[_subscriber putCompletion];
}
- (void)dispose
{
[_currentDisposable dispose];
[_disposable dispose];
}
@end
@implementation SSignal (Meta)
- (SSignal *)switchToLatest
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:false throttleMode:false];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
- (SSignal *)mapToSignal:(SSignal *(^)(id))f
{
return [[self map:f] switchToLatest];
}
- (SSignal *)mapToQueue:(SSignal *(^)(id))f
{
return [[self map:f] queue];
}
- (SSignal *)mapToThrottled:(SSignal *(^)(id))f {
return [[self map:f] throttled];
}
- (SSignal *)then:(SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
[compositeDisposable add:currentDisposable];
[currentDisposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[compositeDisposable add:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}]];
return compositeDisposable;
}];
}
- (SSignal *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:false];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
- (SSignal *)throttled {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:true];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
+ (SSignal *)defer:(SSignal *(^)())generator
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
return [generator() startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
@end
@interface SSignalQueue () {
SPipe *_pipe;
id<SDisposable> _disposable;
}
@end
@implementation SSignalQueue
- (instancetype)init {
self = [super init];
if (self != nil) {
_pipe = [[SPipe alloc] init];
_disposable = [[_pipe.signalProducer() queue] startWithNext:nil];
}
return self;
}
- (void)dealloc {
[_disposable dispose];
}
- (SSignal *)enqueue:(SSignal *)signal {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SPipe *disposePipe = [[SPipe alloc] init];
SSignal *proxy = [[[[signal onNext:^(id next) {
[subscriber putNext:next];
}] onError:^(id error) {
[subscriber putError:error];
}] onCompletion:^{
[subscriber putCompletion];
}] catch:^SSignal *(__unused id error) {
return [SSignal complete];
}];
_pipe.sink([proxy takeUntilReplacement:disposePipe.signalProducer()]);
return [[SBlockDisposable alloc] initWithBlock:^{
disposePipe.sink([SSignal complete]);
}];
}];
}
@end
@@ -0,0 +1,7 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Multicast)
- (SSignal *)multicast;
@end
@@ -0,0 +1,158 @@
#import "SSignal+Multicast.h"
#import <libkern/OSAtomic.h>
#import "SBag.h"
#import "SBlockDisposable.h"
typedef enum {
SSignalMulticastStateReady,
SSignalMulticastStateStarted,
SSignalMulticastStateCompleted
} SSignalMulticastState;
@interface SSignalMulticastSubscribers : NSObject
{
volatile OSSpinLock _lock;
SBag *_subscribers;
SSignalMulticastState _state;
id<SDisposable> _disposable;
}
@end
@implementation SSignalMulticastSubscribers
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_subscribers = [[SBag alloc] init];
}
return self;
}
- (void)setDisposable:(id<SDisposable>)disposable
{
[_disposable dispose];
_disposable = disposable;
}
- (id<SDisposable>)addSubscriber:(SSubscriber *)subscriber start:(bool *)start
{
OSSpinLockLock(&_lock);
NSInteger index = [_subscribers addItem:subscriber];
switch (_state) {
case SSignalMulticastStateReady:
*start = true;
_state = SSignalMulticastStateStarted;
break;
default:
break;
}
OSSpinLockUnlock(&_lock);
return [[SBlockDisposable alloc] initWithBlock:^
{
[self remove:index];
}];
}
- (void)remove:(NSInteger)index
{
id<SDisposable> currentDisposable = nil;
OSSpinLockLock(&_lock);
[_subscribers removeItem:index];
switch (_state) {
case SSignalMulticastStateStarted:
if ([_subscribers isEmpty])
{
currentDisposable = _disposable;
_disposable = nil;
}
break;
default:
break;
}
OSSpinLockUnlock(&_lock);
[currentDisposable dispose];
}
- (void)notifyNext:(id)next
{
NSArray *currentSubscribers = nil;
OSSpinLockLock(&_lock);
currentSubscribers = [_subscribers copyItems];
OSSpinLockUnlock(&_lock);
for (SSubscriber *subscriber in currentSubscribers)
{
[subscriber putNext:next];
}
}
- (void)notifyError:(id)error
{
NSArray *currentSubscribers = nil;
OSSpinLockLock(&_lock);
currentSubscribers = [_subscribers copyItems];
_state = SSignalMulticastStateCompleted;
OSSpinLockUnlock(&_lock);
for (SSubscriber *subscriber in currentSubscribers)
{
[subscriber putError:error];
}
}
- (void)notifyCompleted
{
NSArray *currentSubscribers = nil;
OSSpinLockLock(&_lock);
currentSubscribers = [_subscribers copyItems];
_state = SSignalMulticastStateCompleted;
OSSpinLockUnlock(&_lock);
for (SSubscriber *subscriber in currentSubscribers)
{
[subscriber putCompletion];
}
}
@end
@implementation SSignal (Multicast)
- (SSignal *)multicast
{
SSignalMulticastSubscribers *subscribers = [[SSignalMulticastSubscribers alloc] init];
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
bool start = false;
id<SDisposable> currentDisposable = [subscribers addSubscriber:subscriber start:&start];
if (start)
{
id<SDisposable> disposable = [self startWithNext:^(id next)
{
[subscribers notifyNext:next];
} error:^(id error)
{
[subscribers notifyError:error];
} completed:^
{
[subscribers notifyCompleted];
}];
[subscribers setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
[disposable dispose];
}]];
}
return currentDisposable;
}];
}
@end
@@ -0,0 +1,11 @@
#import <SSignalKit/SSignalKit.h>
@interface SPipe : NSObject
@property (nonatomic, copy, readonly) SSignal *(^signalProducer)();
@property (nonatomic, copy, readonly) void (^sink)(id);
- (instancetype)initWithReplay:(bool)replay;
@end
@@ -0,0 +1,103 @@
#import "SSignal+Pipe.h"
#import "SBlockDisposable.h"
#import "SAtomic.h"
#import "SBag.h"
@interface SPipeReplayState : NSObject
@property (nonatomic, readonly) bool hasReceivedValue;
@property (nonatomic, strong, readonly) id recentValue;
@end
@implementation SPipeReplayState
- (instancetype)initWithReceivedValue:(bool)receivedValue recentValue:(id)recentValue
{
self = [super init];
if (self != nil)
{
_hasReceivedValue = receivedValue;
_recentValue = recentValue;
}
return self;
}
@end
@implementation SPipe
- (instancetype)init
{
return [self initWithReplay:false];
}
- (instancetype)initWithReplay:(bool)replay
{
self = [super init];
if (self != nil)
{
SAtomic *subscribers = [[SAtomic alloc] initWithValue:[[SBag alloc] init]];
SAtomic *replayState = replay ? [[SAtomic alloc] initWithValue:[[SPipeReplayState alloc] initWithReceivedValue:false recentValue:nil]] : nil;
_signalProducer = [^SSignal *
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
__block NSUInteger index = 0;
[subscribers with:^id(SBag *bag)
{
index = [bag addItem:[^(id next)
{
[subscriber putNext:next];
} copy]];
return nil;
}];
if (replay)
{
[replayState with:^id(SPipeReplayState *state)
{
if (state.hasReceivedValue)
[subscriber putNext:state.recentValue];
return nil;
}];
}
return [[SBlockDisposable alloc] initWithBlock:^
{
[subscribers with:^id(SBag *bag)
{
[bag removeItem:index];
return nil;
}];
}];
}];
} copy];
_sink = [^(id next)
{
NSArray *items = [subscribers with:^id(SBag *bag)
{
return [bag copyItems];
}];
for (void (^item)(id) in items)
{
item(next);
}
if (replay)
{
[replayState modify:^id(__unused SPipeReplayState *state)
{
return [[SPipeReplayState alloc] initWithReceivedValue:true recentValue:next];
}];
}
} copy];
}
return self;
}
@end
@@ -0,0 +1,13 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (SideEffects)
- (SSignal *)onStart:(void (^)())f;
- (SSignal *)onNext:(void (^)(id next))f;
- (SSignal *)afterNext:(void (^)(id next))f;
- (SSignal *)onError:(void (^)(id error))f;
- (SSignal *)onCompletion:(void (^)())f;
- (SSignal *)afterCompletion:(void (^)())f;
- (SSignal *)onDispose:(void (^)())f;
@end
@@ -0,0 +1,141 @@
#import "SSignal+SideEffects.h"
#import "SBlockDisposable.h"
#import "SDisposableSet.h"
@implementation SSignal (SideEffects)
- (SSignal *)onStart:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
f();
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onNext:(void (^)(id next))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
f(next);
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)afterNext:(void (^)(id next))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
f(next);
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onError:(void (^)(id error))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
f(error);
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onCompletion:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
f();
[subscriber putCompletion];
}];
}];
}
- (SSignal *)afterCompletion:(void (^)())f {
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
f();
}];
}];
}
- (SSignal *)onDispose:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
[compositeDisposable add:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
[compositeDisposable add:[[SBlockDisposable alloc] initWithBlock:^
{
f();
}]];
return compositeDisposable;
}];
}
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Single)
+ (SSignal *)single:(id)next;
+ (SSignal *)fail:(id)error;
+ (SSignal *)never;
+ (SSignal *)complete;
@end
@@ -0,0 +1,41 @@
#import "SSignal+Single.h"
@implementation SSignal (Single)
+ (SSignal *)single:(id)next
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putNext:next];
[subscriber putCompletion];
return nil;
}];
}
+ (SSignal *)fail:(id)error
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putError:error];
return nil;
}];
}
+ (SSignal *)never
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (__unused SSubscriber *subscriber)
{
return nil;
}];
}
+ (SSignal *)complete
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putCompletion];
return nil;
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignalKit.h>
@interface SSignal (Take)
- (SSignal *)take:(NSUInteger)count;
- (SSignal *)takeLast;
- (SSignal *)takeUntilReplacement:(SSignal *)replacement;
@end
@@ -0,0 +1,122 @@
#import "SSignal+Take.h"
#import "SAtomic.h"
@interface SSignal_ValueContainer : NSObject
@property (nonatomic, strong, readonly) id value;
@end
@implementation SSignal_ValueContainer
- (instancetype)initWithValue:(id)value {
self = [super init];
if (self != nil) {
_value = value;
}
return self;
}
@end
@implementation SSignal (Take)
- (SSignal *)take:(NSUInteger)count
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SAtomic *counter = [[SAtomic alloc] initWithValue:@(0)];
return [self startWithNext:^(id next)
{
__block bool passthrough = false;
__block bool complete = false;
[counter modify:^id(NSNumber *currentCount)
{
NSUInteger updatedCount = [currentCount unsignedIntegerValue] + 1;
if (updatedCount <= count)
passthrough = true;
if (updatedCount == count)
complete = true;
return @(updatedCount);
}];
if (passthrough)
[subscriber putNext:next];
if (complete)
[subscriber putCompletion];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)takeLast
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SAtomic *last = [[SAtomic alloc] initWithValue:nil];
return [self startWithNext:^(id next)
{
[last swap:[[SSignal_ValueContainer alloc] initWithValue:next]];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
SSignal_ValueContainer *value = [last with:^id(id value) {
return value;
}];
if (value != nil)
{
[subscriber putNext:value.value];
}
[subscriber putCompletion];
}];
}];
}
- (SSignal *)takeUntilReplacement:(SSignal *)replacement {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SDisposableSet *disposable = [[SDisposableSet alloc] init];
SMetaDisposable *selfDisposable = [[SMetaDisposable alloc] init];
SMetaDisposable *replacementDisposable = [[SMetaDisposable alloc] init];
[disposable add:selfDisposable];
[disposable add:replacementDisposable];
[disposable add:[replacement startWithNext:^(SSignal *next) {
[selfDisposable dispose];
[replacementDisposable setDisposable:[next startWithNext:^(id next) {
[subscriber putNext:next];
} error:^(id error) {
[subscriber putError:error];
} completed:^{
[subscriber putCompletion];
}]];
} error:^(id error) {
[subscriber putError:error];
} completed:^{
}]];
[selfDisposable setDisposable:[self startWithNext:^(id next) {
[subscriber putNext:next];
} error:^(id error) {
[replacementDisposable dispose];
[subscriber putError:error];
} completed:^{
[replacementDisposable dispose];
[subscriber putCompletion];
}]];
return disposable;
}];
}
@end
@@ -0,0 +1,11 @@
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SQueue.h>
@interface SSignal (Timing)
- (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue;
- (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal;
- (SSignal *)wait:(NSTimeInterval)seconds;
@end
@@ -0,0 +1,109 @@
#import "SSignal+Timing.h"
#import "SMetaDisposable.h"
#import "SDisposableSet.h"
#import "SBlockDisposable.h"
#import "SSignal+Dispatch.h"
#import "STimer.h"
@implementation SSignal (Timing)
- (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^
{
[disposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} queue:queue];
[timer start];
[disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
[timer invalidate];
}]];
return disposable;
}];
}
- (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^
{
[disposable setDisposable:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} queue:queue];
[timer start];
[disposable setDisposable:[self startWithNext:^(id next)
{
[timer invalidate];
[subscriber putNext:next];
} error:^(id error)
{
[timer invalidate];
[subscriber putError:error];
} completed:^
{
[timer invalidate];
[subscriber putCompletion];
}]];
return disposable;
}];
}
- (SSignal *)wait:(NSTimeInterval)seconds
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
id<SDisposable> disposable = [self startWithNext:^(id next)
{
dispatch_semaphore_signal(semaphore);
[subscriber putNext:next];
} error:^(id error)
{
dispatch_semaphore_signal(semaphore);
[subscriber putError:error];
} completed:^
{
dispatch_semaphore_signal(semaphore);
[subscriber putCompletion];
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)));
return disposable;
}];
}
@end
@@ -0,0 +1,18 @@
#import <SSignalKit/SSubscriber.h>
@interface SSignal : NSObject
{
@public
id<SDisposable> (^_generator)(SSubscriber *);
}
- (instancetype)initWithGenerator:(id<SDisposable> (^)(SSubscriber *))generator;
- (id<SDisposable>)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed;
- (id<SDisposable>)startWithNext:(void (^)(id next))next;
- (id<SDisposable>)startWithNext:(void (^)(id next))next completed:(void (^)())completed;
- (SSignal *)trace:(NSString *)name;
@end
@@ -0,0 +1,107 @@
#import "SSignal.h"
#import "SBlockDisposable.h"
@interface SSubscriberDisposable : NSObject <SDisposable>
{
SSubscriber *_subscriber;
id<SDisposable> _disposable;
}
@end
@implementation SSubscriberDisposable
- (instancetype)initWithSubscriber:(SSubscriber *)subscriber disposable:(id<SDisposable>)disposable
{
self = [super init];
if (self != nil)
{
_subscriber = subscriber;
_disposable = disposable;
}
return self;
}
- (void)dispose
{
[_subscriber _markTerminatedWithoutDisposal];
[_disposable dispose];
}
@end
@interface SSignal ()
{
}
@end
@implementation SSignal
- (instancetype)initWithGenerator:(id<SDisposable> (^)(SSubscriber *))generator
{
self = [super init];
if (self != nil)
{
_generator = [generator copy];
}
return self;
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed traceName:(NSString *)traceName
{
STracingSubscriber *subscriber = [[STracingSubscriber alloc] initWithName:traceName next:next error:error completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed
{
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:error completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next
{
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:nil];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next completed:(void (^)())completed
{
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (SSignal *)trace:(NSString *)name
{
#ifdef DEBUG
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
NSString *traceName = [[NSString alloc] initWithFormat:@"%@#0x%x", name, (int)random()];
NSLog(@"trace(%@ start)", traceName);
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
} traceName:traceName];
}];
#else
return self;
#endif
}
@end
@@ -0,0 +1,45 @@
//
// SSignalKit.h
// SSignalKit
//
// Created by Peter on 31/01/15.
// Copyright (c) 2015 Telegram. All rights reserved.
//
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <UIKit/UIKit.h>
#else
#import <Foundation/Foundation.h>
#endif
//! Project version number for SSignalKit.
FOUNDATION_EXPORT double SSignalKitVersionNumber;
//! Project version string for SSignalKit.
FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SSignalKit/PublicHeader.h>
#import <SSignalKit/SAtomic.h>
#import <SSignalKit/SBag.h>
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SSubscriber.h>
#import <SSignalKit/SDisposable.h>
#import <SSignalKit/SDisposableSet.h>
#import <SSignalKit/SBlockDisposable.h>
#import <SSignalKit/SMetaDisposable.h>
#import <SSignalKit/SSignal+Single.h>
#import <SSignalKit/SSignal+Mapping.h>
#import <SSignalKit/SSignal+Multicast.h>
#import <SSignalKit/SSignal+Meta.h>
#import <SSignalKit/SSignal+Accumulate.h>
#import <SSignalKit/SSignal+Dispatch.h>
#import <SSignalKit/SSignal+Catch.h>
#import <SSignalKit/SSignal+SideEffects.h>
#import <SSignalKit/SSignal+Combine.h>
#import <SSignalKit/SSignal+Timing.h>
#import <SSignalKit/SSignal+Take.h>
#import <SSignalKit/SSignal+Pipe.h>
#import <SSignalKit/SMulticastSignalManager.h>
#import <SSignalKit/STimer.h>
#import <SSignalKit/SVariable.h>
@@ -0,0 +1,22 @@
#import <SSignalKit/SDisposable.h>
@interface SSubscriber : NSObject <SDisposable>
{
}
- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed;
- (void)_assignDisposable:(id<SDisposable>)disposable;
- (void)_markTerminatedWithoutDisposal;
- (void)putNext:(id)next;
- (void)putError:(id)error;
- (void)putCompletion;
@end
@interface STracingSubscriber : SSubscriber
- (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed;
@end
@@ -0,0 +1,276 @@
#import "SSubscriber.h"
#import <libkern/OSAtomic.h>
@interface SSubscriberBlocks : NSObject {
@public
void (^_next)(id);
void (^_error)(id);
void (^_completed)();
}
@end
@implementation SSubscriberBlocks
- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed {
self = [super init];
if (self != nil) {
_next = [next copy];
_error = [error copy];
_completed = [completed copy];
}
return self;
}
@end
@interface SSubscriber ()
{
@protected
OSSpinLock _lock;
bool _terminated;
id<SDisposable> _disposable;
SSubscriberBlocks *_blocks;
}
@end
@implementation SSubscriber
- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
{
self = [super init];
if (self != nil)
{
_blocks = [[SSubscriberBlocks alloc] initWithNext:next error:error completed:completed];
}
return self;
}
- (void)_assignDisposable:(id<SDisposable>)disposable
{
bool dispose = false;
OSSpinLockLock(&_lock);
if (_terminated) {
dispose = true;
} else {
_disposable = disposable;
}
OSSpinLockUnlock(&_lock);
if (dispose) {
[disposable dispose];
}
}
- (void)_markTerminatedWithoutDisposal
{
OSSpinLockLock(&_lock);
SSubscriberBlocks *blocks = nil;
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
_terminated = true;
}
OSSpinLockUnlock(&_lock);
if (blocks) {
blocks = nil;
}
}
- (void)putNext:(id)next
{
SSubscriberBlocks *blocks = nil;
OSSpinLockLock(&_lock);
if (!_terminated) {
blocks = _blocks;
}
OSSpinLockUnlock(&_lock);
if (blocks && blocks->_next) {
blocks->_next(next);
}
}
- (void)putError:(id)error
{
bool shouldDispose = false;
SSubscriberBlocks *blocks = nil;
OSSpinLockLock(&_lock);
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
shouldDispose = true;
_terminated = true;
}
OSSpinLockUnlock(&_lock);
if (blocks && blocks->_error) {
blocks->_error(error);
}
if (shouldDispose)
[self->_disposable dispose];
}
- (void)putCompletion
{
bool shouldDispose = false;
SSubscriberBlocks *blocks = nil;
OSSpinLockLock(&_lock);
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
shouldDispose = true;
_terminated = true;
}
OSSpinLockUnlock(&_lock);
if (blocks && blocks->_completed)
blocks->_completed();
if (shouldDispose)
[self->_disposable dispose];
}
- (void)dispose
{
[self->_disposable dispose];
}
@end
@interface STracingSubscriber ()
{
NSString *_name;
}
@end
@implementation STracingSubscriber
- (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
{
self = [super initWithNext:next error:error completed:completed];
if (self != nil)
{
_name = name;
}
return self;
}
/*- (void)_assignDisposable:(id<SDisposable>)disposable
{
if (_terminated)
[disposable dispose];
else
_disposable = disposable;
}
- (void)_markTerminatedWithoutDisposal
{
OSSpinLockLock(&_lock);
if (!_terminated)
{
NSLog(@"trace(%@ terminated)", _name);
_terminated = true;
_next = nil;
_error = nil;
_completed = nil;
}
OSSpinLockUnlock(&_lock);
}
- (void)putNext:(id)next
{
void (^fnext)(id) = nil;
OSSpinLockLock(&_lock);
if (!_terminated)
fnext = self->_next;
OSSpinLockUnlock(&_lock);
if (fnext)
{
NSLog(@"trace(%@ next: %@)", _name, next);
fnext(next);
}
else
NSLog(@"trace(%@ next: %@, not accepted)", _name, next);
}
- (void)putError:(id)error
{
bool shouldDispose = false;
void (^ferror)(id) = nil;
OSSpinLockLock(&_lock);
if (!_terminated)
{
ferror = self->_error;
shouldDispose = true;
self->_next = nil;
self->_error = nil;
self->_completed = nil;
_terminated = true;
}
OSSpinLockUnlock(&_lock);
if (ferror)
{
NSLog(@"trace(%@ error: %@)", _name, error);
ferror(error);
}
else
NSLog(@"trace(%@ error: %@, not accepted)", _name, error);
if (shouldDispose)
[self->_disposable dispose];
}
- (void)putCompletion
{
bool shouldDispose = false;
void (^completed)() = nil;
OSSpinLockLock(&_lock);
if (!_terminated)
{
completed = self->_completed;
shouldDispose = true;
self->_next = nil;
self->_error = nil;
self->_completed = nil;
_terminated = true;
}
OSSpinLockUnlock(&_lock);
if (completed)
{
NSLog(@"trace(%@ completed)", _name);
completed();
}
else
NSLog(@"trace(%@ completed, not accepted)", _name);
if (shouldDispose)
[self->_disposable dispose];
}
- (void)dispose
{
NSLog(@"trace(%@ dispose)", _name);
[self->_disposable dispose];
}*/
@end
@@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
#import <SSignalKit/SThreadPoolTask.h>
#import <SSignalKit/SThreadPoolQueue.h>
@interface SThreadPool : NSObject
- (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority;
- (void)addTask:(SThreadPoolTask *)task;
- (SThreadPoolQueue *)nextQueue;
- (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block;
@end
@@ -0,0 +1,128 @@
#import "SThreadPool.h"
#import <libkern/OSAtomic.h>
#import <pthread.h>
#import "SQueue.h"
@interface SThreadPool ()
{
SQueue *_managementQueue;
NSMutableArray *_threads;
NSMutableArray *_queues;
NSMutableArray *_takenQueues;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
}
@end
@implementation SThreadPool
+ (void)threadEntryPoint:(SThreadPool *)threadPool
{
SThreadPoolQueue *queue = nil;
while (true)
{
SThreadPoolTask *task = nil;
pthread_mutex_lock(&threadPool->_mutex);
if (queue != nil)
{
[threadPool->_takenQueues removeObject:queue];
if ([queue _hasTasks])
[threadPool->_queues addObject:queue];
}
while (true)
{
while (threadPool->_queues.count == 0)
pthread_cond_wait(&threadPool->_cond, &threadPool->_mutex);
queue = threadPool->_queues.firstObject;
task = [queue _popFirstTask];
if (queue != nil)
{
[threadPool->_takenQueues addObject:queue];
[threadPool->_queues removeObjectAtIndex:0];
break;
}
}
pthread_mutex_unlock(&threadPool->_mutex);
@autoreleasepool
{
[task execute];
}
}
}
- (instancetype)init
{
return [self initWithThreadCount:2 threadPriority:0.5];
}
- (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority
{
self = [super init];
if (self != nil)
{
pthread_mutex_init(&_mutex, 0);
pthread_cond_init(&_cond, 0);
_managementQueue = [[SQueue alloc] init];
[_managementQueue dispatch:^
{
_threads = [[NSMutableArray alloc] init];
_queues = [[NSMutableArray alloc] init];
_takenQueues = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < threadCount; i++)
{
NSThread *thread = [[NSThread alloc] initWithTarget:[SThreadPool class] selector:@selector(threadEntryPoint:) object:self];
thread.name = [[NSString alloc] initWithFormat:@"SThreadPool-%p-%d", self, (int)i];
[thread setThreadPriority:threadPriority];
[_threads addObject:thread];
[thread start];
}
}];
}
return self;
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
- (void)addTask:(SThreadPoolTask *)task
{
SThreadPoolQueue *tempQueue = [self nextQueue];
[tempQueue addTask:task];
}
- (SThreadPoolQueue *)nextQueue
{
return [[SThreadPoolQueue alloc] initWithThreadPool:self];
}
- (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block
{
[_managementQueue dispatch:^
{
pthread_mutex_lock(&_mutex);
block();
if (![_queues containsObject:queue] && ![_takenQueues containsObject:queue])
[_queues addObject:queue];
pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}];
}
@end
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@class SThreadPool;
@class SThreadPoolTask;
@interface SThreadPoolQueue : NSObject
- (instancetype)initWithThreadPool:(SThreadPool *)threadPool;
- (void)addTask:(SThreadPoolTask *)task;
- (SThreadPoolTask *)_popFirstTask;
- (bool)_hasTasks;
@end
@@ -0,0 +1,51 @@
#import "SThreadPoolQueue.h"
#import "SThreadPool.h"
@interface SThreadPoolQueue ()
{
__weak SThreadPool *_threadPool;
NSMutableArray *_tasks;
}
@end
@implementation SThreadPoolQueue
- (instancetype)initWithThreadPool:(SThreadPool *)threadPool
{
self = [super init];
if (self != nil)
{
_threadPool = threadPool;
_tasks = [[NSMutableArray alloc] init];
}
return self;
}
- (void)addTask:(SThreadPoolTask *)task
{
SThreadPool *threadPool = _threadPool;
[threadPool _workOnQueue:self block:^
{
[_tasks addObject:task];
}];
}
- (SThreadPoolTask *)_popFirstTask
{
if (_tasks.count != 0)
{
SThreadPoolTask *task = _tasks[0];
[_tasks removeObjectAtIndex:0];
return task;
}
return nil;
}
- (bool)_hasTasks
{
return _tasks.count != 0;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface SThreadPoolTask : NSObject
- (instancetype)initWithBlock:(void (^)(bool (^)()))block;
- (void)execute;
- (void)cancel;
@end
@@ -0,0 +1,53 @@
#import "SThreadPoolTask.h"
@interface SThreadPoolTaskState : NSObject
{
@public
bool _cancelled;
}
@end
@implementation SThreadPoolTaskState
@end
@interface SThreadPoolTask ()
{
void (^_block)(bool (^)());
SThreadPoolTaskState *_state;
}
@end
@implementation SThreadPoolTask
- (instancetype)initWithBlock:(void (^)(bool (^)()))block
{
self = [super init];
if (self != nil)
{
_block = [block copy];
_state = [[SThreadPoolTaskState alloc] init];
}
return self;
}
- (void)execute
{
if (_state->_cancelled)
return;
SThreadPoolTaskState *state = _state;
_block(^bool
{
return state->_cancelled;
});
}
- (void)cancel
{
_state->_cancelled = true;
}
@end
@@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>
@class SQueue;
@interface STimer : NSObject
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue;
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue;
- (void)start;
- (void)invalidate;
- (void)fireAndInvalidate;
@end
@@ -0,0 +1,83 @@
#import "STimer.h"
#import "SQueue.h"
@interface STimer ()
{
dispatch_source_t _timer;
NSTimeInterval _timeout;
NSTimeInterval _timeoutDate;
bool _repeat;
dispatch_block_t _completion;
dispatch_queue_t _nativeQueue;
}
@end
@implementation STimer
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue {
return [self initWithTimeout:timeout repeat:repeat completion:completion nativeQueue:queue._dispatch_queue];
}
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue
{
self = [super init];
if (self != nil)
{
_timeoutDate = INT_MAX;
_timeout = timeout;
_repeat = repeat;
_completion = [completion copy];
_nativeQueue = nativeQueue;
}
return self;
}
- (void)dealloc
{
if (_timer != nil)
{
dispatch_source_cancel(_timer);
_timer = nil;
}
}
- (void)start
{
_timeoutDate = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 + _timeout;
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _nativeQueue);
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);
dispatch_source_set_event_handler(_timer, ^
{
if (_completion)
_completion();
if (!_repeat)
[self invalidate];
});
dispatch_resume(_timer);
}
- (void)fireAndInvalidate
{
if (_completion)
_completion();
[self invalidate];
}
- (void)invalidate
{
_timeoutDate = 0;
if (_timer != nil)
{
dispatch_source_cancel(_timer);
_timer = nil;
}
}
@end
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@class SSignal;
@interface SVariable : NSObject
- (instancetype)init;
- (void)set:(SSignal *)signal;
- (SSignal *)signal;
@end
@@ -0,0 +1,93 @@
#import "SVariable.h"
#import <libkern/OSAtomic.h>
#import "SSignal.h"
#import "SBag.h"
#import "SBlockDisposable.h"
#import "SMetaDisposable.h"
@interface SVariable ()
{
OSSpinLock _lock;
id _value;
bool _hasValue;
SBag *_subscribers;
SMetaDisposable *_disposable;
}
@end
@implementation SVariable
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_subscribers = [[SBag alloc] init];
_disposable = [[SMetaDisposable alloc] init];
}
return self;
}
- (void)dealloc
{
[_disposable dispose];
}
- (SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
OSSpinLockLock(&self->_lock);
id currentValue = _value;
bool hasValue = _hasValue;
NSInteger index = [self->_subscribers addItem:[^(id value)
{
[subscriber putNext:value];
} copy]];
OSSpinLockUnlock(&self->_lock);
if (hasValue)
{
[subscriber putNext:currentValue];
}
return [[SBlockDisposable alloc] initWithBlock:^
{
OSSpinLockLock(&self->_lock);
[self->_subscribers removeItem:index];
OSSpinLockUnlock(&self->_lock);
}];
}];
}
- (void)set:(SSignal *)signal
{
OSSpinLockLock(&_lock);
_hasValue = false;
OSSpinLockUnlock(&_lock);
__weak SVariable *weakSelf = self;
[_disposable setDisposable:[signal startWithNext:^(id next)
{
__strong SVariable *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSArray *subscribers = nil;
OSSpinLockLock(&strongSelf->_lock);
strongSelf->_value = next;
strongSelf->_hasValue = true;
subscribers = [strongSelf->_subscribers copyItems];
OSSpinLockUnlock(&strongSelf->_lock);
for (void (^subscriber)(id) in subscribers)
{
subscriber(next);
}
}
}]];
}
@end