mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Add 'submodules/SSignalKit/' from commit '359b2ee7c9f20f99f221f78e307369ef5ad0ece2'
git-subtree-dir: submodules/SSignalKit git-subtree-mainline:4459dc5b47git-subtree-split:359b2ee7c9
This commit is contained in:
25
submodules/SSignalKit/.gitignore
vendored
Normal file
25
submodules/SSignalKit/.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
fastlane/README.md
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/test_output/*
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata
|
||||||
|
*.xccheckout
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.moved-aside
|
||||||
|
DerivedData
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.xcuserstate
|
||||||
|
.DS_Store
|
||||||
|
*.dSYM
|
||||||
|
*.dSYM.zip
|
||||||
|
*.ipa
|
||||||
|
*/xcuserdata/*
|
||||||
|
SSignalKit.xcodeproj/*
|
||||||
37
submodules/SSignalKit/BUCK
Normal file
37
submodules/SSignalKit/BUCK
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
load('//tools:buck_utils.bzl', 'config_with_updated_linker_flags', 'configs_with_config', 'combined_config')
|
||||||
|
load('//tools:buck_defs.bzl', 'SHARED_CONFIGS', 'EXTENSION_LIB_SPECIFIC_CONFIG')
|
||||||
|
|
||||||
|
apple_library(
|
||||||
|
name = 'SwiftSignalKit',
|
||||||
|
srcs = glob([
|
||||||
|
'SwiftSignalKit/*.swift'
|
||||||
|
]),
|
||||||
|
configs = configs_with_config(combined_config([SHARED_CONFIGS, EXTENSION_LIB_SPECIFIC_CONFIG])),
|
||||||
|
modular = True,
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
frameworks = [
|
||||||
|
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
apple_library(
|
||||||
|
name = 'SSignalKit',
|
||||||
|
srcs = glob([
|
||||||
|
'SSignalKit/*.m',
|
||||||
|
]),
|
||||||
|
configs = configs_with_config(combined_config([SHARED_CONFIGS, EXTENSION_LIB_SPECIFIC_CONFIG])),
|
||||||
|
headers = glob([
|
||||||
|
'SSignalKit/*.h',
|
||||||
|
]),
|
||||||
|
header_namespace = 'SSignalKit',
|
||||||
|
exported_headers = glob([
|
||||||
|
'SSignalKit/*.h',
|
||||||
|
]),
|
||||||
|
modular = True,
|
||||||
|
compiler_flags = ['-w'],
|
||||||
|
preprocessor_flags = ['-fobjc-arc'],
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
frameworks = [
|
||||||
|
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
|
||||||
|
],
|
||||||
|
)
|
||||||
26
submodules/SSignalKit/SSignalKit/Info.plist
Normal file
26
submodules/SSignalKit/SSignalKit/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
12
submodules/SSignalKit/SSignalKit/SAtomic.h
Normal file
12
submodules/SSignalKit/SSignalKit/SAtomic.h
Normal file
@@ -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
|
||||||
93
submodules/SSignalKit/SSignalKit/SAtomic.m
Normal file
93
submodules/SSignalKit/SSignalKit/SAtomic.m
Normal file
@@ -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
|
||||||
11
submodules/SSignalKit/SSignalKit/SBag.h
Normal file
11
submodules/SSignalKit/SSignalKit/SBag.h
Normal file
@@ -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
|
||||||
74
submodules/SSignalKit/SSignalKit/SBag.m
Normal file
74
submodules/SSignalKit/SSignalKit/SBag.m
Normal file
@@ -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
|
||||||
7
submodules/SSignalKit/SSignalKit/SBlockDisposable.h
Normal file
7
submodules/SSignalKit/SSignalKit/SBlockDisposable.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import <SSignalKit/SDisposable.h>
|
||||||
|
|
||||||
|
@interface SBlockDisposable : NSObject <SDisposable>
|
||||||
|
|
||||||
|
- (instancetype)initWithBlock:(void (^)())block;
|
||||||
|
|
||||||
|
@end
|
||||||
58
submodules/SSignalKit/SSignalKit/SBlockDisposable.m
Normal file
58
submodules/SSignalKit/SSignalKit/SBlockDisposable.m
Normal file
@@ -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
|
||||||
7
submodules/SSignalKit/SSignalKit/SDisposable.h
Normal file
7
submodules/SSignalKit/SSignalKit/SDisposable.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@protocol SDisposable <NSObject>
|
||||||
|
|
||||||
|
- (void)dispose;
|
||||||
|
|
||||||
|
@end
|
||||||
10
submodules/SSignalKit/SSignalKit/SDisposableSet.h
Normal file
10
submodules/SSignalKit/SSignalKit/SDisposableSet.h
Normal file
@@ -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
|
||||||
95
submodules/SSignalKit/SSignalKit/SDisposableSet.m
Normal file
95
submodules/SSignalKit/SSignalKit/SDisposableSet.m
Normal file
@@ -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
|
||||||
7
submodules/SSignalKit/SSignalKit/SMetaDisposable.h
Normal file
7
submodules/SSignalKit/SSignalKit/SMetaDisposable.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import <SSignalKit/SDisposable.h>
|
||||||
|
|
||||||
|
@interface SMetaDisposable : NSObject <SDisposable>
|
||||||
|
|
||||||
|
- (void)setDisposable:(id<SDisposable>)disposable;
|
||||||
|
|
||||||
|
@end
|
||||||
53
submodules/SSignalKit/SSignalKit/SMetaDisposable.m
Normal file
53
submodules/SSignalKit/SSignalKit/SMetaDisposable.m
Normal file
@@ -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
|
||||||
11
submodules/SSignalKit/SSignalKit/SMulticastSignalManager.h
Normal file
11
submodules/SSignalKit/SSignalKit/SMulticastSignalManager.h
Normal file
@@ -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
|
||||||
171
submodules/SSignalKit/SSignalKit/SMulticastSignalManager.m
Normal file
171
submodules/SSignalKit/SSignalKit/SMulticastSignalManager.m
Normal file
@@ -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
|
||||||
19
submodules/SSignalKit/SSignalKit/SQueue.h
Normal file
19
submodules/SSignalKit/SSignalKit/SQueue.h
Normal file
@@ -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
|
||||||
124
submodules/SSignalKit/SSignalKit/SQueue.m
Normal file
124
submodules/SSignalKit/SSignalKit/SQueue.m
Normal file
@@ -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
|
||||||
8
submodules/SSignalKit/SSignalKit/SSignal+Accumulate.h
Normal file
8
submodules/SSignalKit/SSignalKit/SSignal+Accumulate.h
Normal file
@@ -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
|
||||||
52
submodules/SSignalKit/SSignalKit/SSignal+Accumulate.m
Normal file
52
submodules/SSignalKit/SSignalKit/SSignal+Accumulate.m
Normal file
@@ -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
|
||||||
9
submodules/SSignalKit/SSignalKit/SSignal+Catch.h
Normal file
9
submodules/SSignalKit/SSignalKit/SSignal+Catch.h
Normal file
@@ -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
|
||||||
147
submodules/SSignalKit/SSignalKit/SSignal+Catch.m
Normal file
147
submodules/SSignalKit/SSignalKit/SSignal+Catch.m
Normal file
@@ -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
|
||||||
10
submodules/SSignalKit/SSignalKit/SSignal+Combine.h
Normal file
10
submodules/SSignalKit/SSignalKit/SSignal+Combine.h
Normal file
@@ -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
|
||||||
177
submodules/SSignalKit/SSignalKit/SSignal+Combine.m
Normal file
177
submodules/SSignalKit/SSignalKit/SSignal+Combine.m
Normal file
@@ -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
|
||||||
14
submodules/SSignalKit/SSignalKit/SSignal+Dispatch.h
Normal file
14
submodules/SSignalKit/SSignalKit/SSignal+Dispatch.h
Normal file
@@ -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
|
||||||
212
submodules/SSignalKit/SSignalKit/SSignal+Dispatch.m
Normal file
212
submodules/SSignalKit/SSignalKit/SSignal+Dispatch.m
Normal file
@@ -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
|
||||||
9
submodules/SSignalKit/SSignalKit/SSignal+Mapping.h
Normal file
9
submodules/SSignalKit/SSignalKit/SSignal+Mapping.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
|
@interface SSignal (Mapping)
|
||||||
|
|
||||||
|
- (SSignal *)map:(id (^)(id))f;
|
||||||
|
- (SSignal *)filter:(bool (^)(id))f;
|
||||||
|
- (SSignal *)ignoreRepeated;
|
||||||
|
|
||||||
|
@end
|
||||||
83
submodules/SSignalKit/SSignalKit/SSignal+Mapping.m
Normal file
83
submodules/SSignalKit/SSignalKit/SSignal+Mapping.m
Normal file
@@ -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
|
||||||
22
submodules/SSignalKit/SSignalKit/SSignal+Meta.h
Normal file
22
submodules/SSignalKit/SSignalKit/SSignal+Meta.h
Normal file
@@ -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
|
||||||
325
submodules/SSignalKit/SSignalKit/SSignal+Meta.m
Normal file
325
submodules/SSignalKit/SSignalKit/SSignal+Meta.m
Normal file
@@ -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
|
||||||
7
submodules/SSignalKit/SSignalKit/SSignal+Multicast.h
Normal file
7
submodules/SSignalKit/SSignalKit/SSignal+Multicast.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
|
@interface SSignal (Multicast)
|
||||||
|
|
||||||
|
- (SSignal *)multicast;
|
||||||
|
|
||||||
|
@end
|
||||||
158
submodules/SSignalKit/SSignalKit/SSignal+Multicast.m
Normal file
158
submodules/SSignalKit/SSignalKit/SSignal+Multicast.m
Normal file
@@ -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
|
||||||
11
submodules/SSignalKit/SSignalKit/SSignal+Pipe.h
Normal file
11
submodules/SSignalKit/SSignalKit/SSignal+Pipe.h
Normal file
@@ -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
|
||||||
|
|
||||||
103
submodules/SSignalKit/SSignalKit/SSignal+Pipe.m
Normal file
103
submodules/SSignalKit/SSignalKit/SSignal+Pipe.m
Normal file
@@ -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
|
||||||
13
submodules/SSignalKit/SSignalKit/SSignal+SideEffects.h
Normal file
13
submodules/SSignalKit/SSignalKit/SSignal+SideEffects.h
Normal file
@@ -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
|
||||||
141
submodules/SSignalKit/SSignalKit/SSignal+SideEffects.m
Normal file
141
submodules/SSignalKit/SSignalKit/SSignal+SideEffects.m
Normal file
@@ -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
|
||||||
10
submodules/SSignalKit/SSignalKit/SSignal+Single.h
Normal file
10
submodules/SSignalKit/SSignalKit/SSignal+Single.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
|
@interface SSignal (Single)
|
||||||
|
|
||||||
|
+ (SSignal *)single:(id)next;
|
||||||
|
+ (SSignal *)fail:(id)error;
|
||||||
|
+ (SSignal *)never;
|
||||||
|
+ (SSignal *)complete;
|
||||||
|
|
||||||
|
@end
|
||||||
41
submodules/SSignalKit/SSignalKit/SSignal+Single.m
Normal file
41
submodules/SSignalKit/SSignalKit/SSignal+Single.m
Normal file
@@ -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
|
||||||
9
submodules/SSignalKit/SSignalKit/SSignal+Take.h
Normal file
9
submodules/SSignalKit/SSignalKit/SSignal+Take.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#import <SSignalKit/SSignalKit.h>
|
||||||
|
|
||||||
|
@interface SSignal (Take)
|
||||||
|
|
||||||
|
- (SSignal *)take:(NSUInteger)count;
|
||||||
|
- (SSignal *)takeLast;
|
||||||
|
- (SSignal *)takeUntilReplacement:(SSignal *)replacement;
|
||||||
|
|
||||||
|
@end
|
||||||
122
submodules/SSignalKit/SSignalKit/SSignal+Take.m
Normal file
122
submodules/SSignalKit/SSignalKit/SSignal+Take.m
Normal file
@@ -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
|
||||||
11
submodules/SSignalKit/SSignalKit/SSignal+Timing.h
Normal file
11
submodules/SSignalKit/SSignalKit/SSignal+Timing.h
Normal file
@@ -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
|
||||||
109
submodules/SSignalKit/SSignalKit/SSignal+Timing.m
Normal file
109
submodules/SSignalKit/SSignalKit/SSignal+Timing.m
Normal file
@@ -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
|
||||||
18
submodules/SSignalKit/SSignalKit/SSignal.h
Normal file
18
submodules/SSignalKit/SSignalKit/SSignal.h
Normal file
@@ -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
|
||||||
|
|
||||||
107
submodules/SSignalKit/SSignalKit/SSignal.m
Normal file
107
submodules/SSignalKit/SSignalKit/SSignal.m
Normal file
@@ -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
|
||||||
45
submodules/SSignalKit/SSignalKit/SSignalKit.h
Normal file
45
submodules/SSignalKit/SSignalKit/SSignalKit.h
Normal file
@@ -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>
|
||||||
22
submodules/SSignalKit/SSignalKit/SSubscriber.h
Normal file
22
submodules/SSignalKit/SSignalKit/SSubscriber.h
Normal file
@@ -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
|
||||||
276
submodules/SSignalKit/SSignalKit/SSubscriber.m
Normal file
276
submodules/SSignalKit/SSignalKit/SSubscriber.m
Normal file
@@ -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
|
||||||
15
submodules/SSignalKit/SSignalKit/SThreadPool.h
Normal file
15
submodules/SSignalKit/SSignalKit/SThreadPool.h
Normal file
@@ -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
|
||||||
128
submodules/SSignalKit/SSignalKit/SThreadPool.m
Normal file
128
submodules/SSignalKit/SSignalKit/SThreadPool.m
Normal file
@@ -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
|
||||||
13
submodules/SSignalKit/SSignalKit/SThreadPoolQueue.h
Normal file
13
submodules/SSignalKit/SSignalKit/SThreadPoolQueue.h
Normal file
@@ -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
|
||||||
51
submodules/SSignalKit/SSignalKit/SThreadPoolQueue.m
Normal file
51
submodules/SSignalKit/SSignalKit/SThreadPoolQueue.m
Normal file
@@ -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
|
||||||
9
submodules/SSignalKit/SSignalKit/SThreadPoolTask.h
Normal file
9
submodules/SSignalKit/SSignalKit/SThreadPoolTask.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface SThreadPoolTask : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithBlock:(void (^)(bool (^)()))block;
|
||||||
|
- (void)execute;
|
||||||
|
- (void)cancel;
|
||||||
|
|
||||||
|
@end
|
||||||
53
submodules/SSignalKit/SSignalKit/SThreadPoolTask.m
Normal file
53
submodules/SSignalKit/SSignalKit/SThreadPoolTask.m
Normal file
@@ -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
|
||||||
14
submodules/SSignalKit/SSignalKit/STimer.h
Normal file
14
submodules/SSignalKit/SSignalKit/STimer.h
Normal file
@@ -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
|
||||||
83
submodules/SSignalKit/SSignalKit/STimer.m
Normal file
83
submodules/SSignalKit/SSignalKit/STimer.m
Normal file
@@ -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
|
||||||
12
submodules/SSignalKit/SSignalKit/SVariable.h
Normal file
12
submodules/SSignalKit/SSignalKit/SVariable.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@class SSignal;
|
||||||
|
|
||||||
|
@interface SVariable : NSObject
|
||||||
|
|
||||||
|
- (instancetype)init;
|
||||||
|
|
||||||
|
- (void)set:(SSignal *)signal;
|
||||||
|
- (SSignal *)signal;
|
||||||
|
|
||||||
|
@end
|
||||||
93
submodules/SSignalKit/SSignalKit/SVariable.m
Normal file
93
submodules/SSignalKit/SSignalKit/SVariable.m
Normal file
@@ -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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface DeallocatingObject : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithDeallocated:(bool *)deallocated;
|
||||||
|
|
||||||
|
@end
|
||||||
27
submodules/SSignalKit/SSignalKitTests/DeallocatingObject.m
Normal file
27
submodules/SSignalKit/SSignalKitTests/DeallocatingObject.m
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#import "DeallocatingObject.h"
|
||||||
|
|
||||||
|
@interface DeallocatingObject ()
|
||||||
|
{
|
||||||
|
bool *_deallocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DeallocatingObject
|
||||||
|
|
||||||
|
- (instancetype)initWithDeallocated:(bool *)deallocated
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil)
|
||||||
|
{
|
||||||
|
_deallocated = deallocated;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
*_deallocated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
24
submodules/SSignalKit/SSignalKitTests/Info.plist
Normal file
24
submodules/SSignalKit/SSignalKitTests/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
318
submodules/SSignalKit/SSignalKitTests/SDisposableTests.m
Normal file
318
submodules/SSignalKit/SSignalKitTests/SDisposableTests.m
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#else
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#endif
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import <libkern/OSAtomic.h>
|
||||||
|
|
||||||
|
@import SSignalKit;
|
||||||
|
|
||||||
|
#import "DeallocatingObject.h"
|
||||||
|
|
||||||
|
@interface SDisposableTests : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDisposableTests
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
[super tearDown];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testBlockDisposableDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *disposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
object = nil;
|
||||||
|
block = nil;
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testBlockDisposableNotDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *disposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
[disposable description];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertFalse(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMetaDisposableDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
|
||||||
|
SMetaDisposable *metaDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
[metaDisposable setDisposable:blockDisposable];
|
||||||
|
[metaDisposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMetaDisposableDisposedMultipleTimes
|
||||||
|
{
|
||||||
|
bool deallocated1 = false;
|
||||||
|
__block bool disposed1 = false;
|
||||||
|
bool deallocated2 = false;
|
||||||
|
__block bool disposed2 = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object1 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated1];
|
||||||
|
dispatch_block_t block1 = ^{
|
||||||
|
[object1 description];
|
||||||
|
disposed1 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable1 = [[SBlockDisposable alloc] initWithBlock:[block1 copy]];
|
||||||
|
|
||||||
|
DeallocatingObject *object2 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated2];
|
||||||
|
dispatch_block_t block2 = ^{
|
||||||
|
[object2 description];
|
||||||
|
disposed2 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable2 = [[SBlockDisposable alloc] initWithBlock:[block2 copy]];
|
||||||
|
|
||||||
|
SMetaDisposable *metaDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
[metaDisposable setDisposable:blockDisposable1];
|
||||||
|
[metaDisposable setDisposable:blockDisposable2];
|
||||||
|
[metaDisposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated1);
|
||||||
|
XCTAssertTrue(disposed1);
|
||||||
|
XCTAssertTrue(deallocated2);
|
||||||
|
XCTAssertTrue(disposed2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMetaDisposableNotDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
|
||||||
|
SMetaDisposable *metaDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
[metaDisposable setDisposable:blockDisposable];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertFalse(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableSetSingleDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
|
||||||
|
SDisposableSet *disposableSet = [[SDisposableSet alloc] init];
|
||||||
|
[disposableSet add:blockDisposable];
|
||||||
|
[disposableSet dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableSetMultipleDisposed
|
||||||
|
{
|
||||||
|
bool deallocated1 = false;
|
||||||
|
__block bool disposed1 = false;
|
||||||
|
bool deallocated2 = false;
|
||||||
|
__block bool disposed2 = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object1 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated1];
|
||||||
|
dispatch_block_t block1 = ^{
|
||||||
|
[object1 description];
|
||||||
|
disposed1 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable1 = [[SBlockDisposable alloc] initWithBlock:[block1 copy]];
|
||||||
|
|
||||||
|
DeallocatingObject *object2 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated2];
|
||||||
|
dispatch_block_t block2 = ^{
|
||||||
|
[object2 description];
|
||||||
|
disposed2 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable2 = [[SBlockDisposable alloc] initWithBlock:[block2 copy]];
|
||||||
|
|
||||||
|
SDisposableSet *disposableSet = [[SDisposableSet alloc] init];
|
||||||
|
[disposableSet add:blockDisposable1];
|
||||||
|
[disposableSet add:blockDisposable2];
|
||||||
|
[disposableSet dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated1);
|
||||||
|
XCTAssertTrue(disposed1);
|
||||||
|
XCTAssertTrue(deallocated2);
|
||||||
|
XCTAssertTrue(disposed2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableSetSingleNotDisposed
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable = [[SBlockDisposable alloc] initWithBlock:[block copy]];
|
||||||
|
|
||||||
|
SDisposableSet *disposableSet = [[SDisposableSet alloc] init];
|
||||||
|
[disposableSet add:blockDisposable];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertFalse(disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableSetMultipleNotDisposed
|
||||||
|
{
|
||||||
|
bool deallocated1 = false;
|
||||||
|
__block bool disposed1 = false;
|
||||||
|
bool deallocated2 = false;
|
||||||
|
__block bool disposed2 = false;
|
||||||
|
{
|
||||||
|
DeallocatingObject *object1 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated1];
|
||||||
|
dispatch_block_t block1 = ^{
|
||||||
|
[object1 description];
|
||||||
|
disposed1 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable1 = [[SBlockDisposable alloc] initWithBlock:[block1 copy]];
|
||||||
|
|
||||||
|
DeallocatingObject *object2 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated2];
|
||||||
|
dispatch_block_t block2 = ^{
|
||||||
|
[object2 description];
|
||||||
|
disposed2 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable2 = [[SBlockDisposable alloc] initWithBlock:[block2 copy]];
|
||||||
|
|
||||||
|
SDisposableSet *disposableSet = [[SDisposableSet alloc] init];
|
||||||
|
[disposableSet add:blockDisposable1];
|
||||||
|
[disposableSet add:blockDisposable2];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated1);
|
||||||
|
XCTAssertFalse(disposed1);
|
||||||
|
XCTAssertTrue(deallocated2);
|
||||||
|
XCTAssertFalse(disposed2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMetaDisposableAlreadyDisposed
|
||||||
|
{
|
||||||
|
bool deallocated1 = false;
|
||||||
|
__block bool disposed1 = false;
|
||||||
|
bool deallocated2 = false;
|
||||||
|
__block bool disposed2 = false;
|
||||||
|
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
DeallocatingObject *object1 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated1];
|
||||||
|
dispatch_block_t block1 = ^{
|
||||||
|
[object1 description];
|
||||||
|
disposed1 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable1 = [[SBlockDisposable alloc] initWithBlock:[block1 copy]];
|
||||||
|
|
||||||
|
DeallocatingObject *object2 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated2];
|
||||||
|
dispatch_block_t block2 = ^{
|
||||||
|
[object2 description];
|
||||||
|
disposed2 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable2 = [[SBlockDisposable alloc] initWithBlock:[block2 copy]];
|
||||||
|
|
||||||
|
SMetaDisposable *metaDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
[metaDisposable setDisposable:blockDisposable1];
|
||||||
|
[metaDisposable dispose];
|
||||||
|
[metaDisposable setDisposable:blockDisposable2];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated1);
|
||||||
|
XCTAssertTrue(disposed1);
|
||||||
|
XCTAssertTrue(deallocated2);
|
||||||
|
XCTAssertTrue(disposed2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableSetAlreadyDisposed
|
||||||
|
{
|
||||||
|
bool deallocated1 = false;
|
||||||
|
__block bool disposed1 = false;
|
||||||
|
bool deallocated2 = false;
|
||||||
|
__block bool disposed2 = false;
|
||||||
|
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
DeallocatingObject *object1 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated1];
|
||||||
|
dispatch_block_t block1 = ^{
|
||||||
|
[object1 description];
|
||||||
|
disposed1 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable1 = [[SBlockDisposable alloc] initWithBlock:[block1 copy]];
|
||||||
|
|
||||||
|
DeallocatingObject *object2 = [[DeallocatingObject alloc] initWithDeallocated:&deallocated2];
|
||||||
|
dispatch_block_t block2 = ^{
|
||||||
|
[object2 description];
|
||||||
|
disposed2 = true;
|
||||||
|
};
|
||||||
|
SBlockDisposable *blockDisposable2 = [[SBlockDisposable alloc] initWithBlock:[block2 copy]];
|
||||||
|
|
||||||
|
SMetaDisposable *metaDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
[metaDisposable setDisposable:blockDisposable1];
|
||||||
|
[metaDisposable dispose];
|
||||||
|
[metaDisposable setDisposable:blockDisposable2];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated1);
|
||||||
|
XCTAssertTrue(disposed1);
|
||||||
|
XCTAssertTrue(deallocated2);
|
||||||
|
XCTAssertTrue(disposed2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
764
submodules/SSignalKit/SSignalKitTests/SSignalBasicTests.m
Normal file
764
submodules/SSignalKit/SSignalKitTests/SSignalBasicTests.m
Normal file
@@ -0,0 +1,764 @@
|
|||||||
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#else
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#endif
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
@import SSignalKit;
|
||||||
|
|
||||||
|
#import "DeallocatingObject.h"
|
||||||
|
|
||||||
|
@interface DisposableHolder : NSObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (nonatomic, strong) id<SDisposable> disposable;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation DisposableHolder
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_disposable = [[[SSignal single:nil] delay:1.0 onQueue:[SQueue concurrentDefaultQueue]] startWithNext:^(__unused id next){
|
||||||
|
[self description];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[_disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface SSignalBasicTests : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SSignalBasicTests
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
[super tearDown];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSignalGenerated
|
||||||
|
{
|
||||||
|
__block bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
__block bool generated = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[object description];
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
id<SDisposable> disposable = [signal startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
generated = true;
|
||||||
|
[object description];
|
||||||
|
} error:nil completed:nil];
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
XCTAssertTrue(generated);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSignalGeneratedCompleted
|
||||||
|
{
|
||||||
|
__block bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
__block bool generated = false;
|
||||||
|
__block bool completed = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
[object description];
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
id<SDisposable> disposable = [signal startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
generated = true;
|
||||||
|
} error:nil completed:^
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
completed = true;
|
||||||
|
}];
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
XCTAssertTrue(generated);
|
||||||
|
XCTAssertTrue(completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSignalGeneratedError
|
||||||
|
{
|
||||||
|
__block bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
__block bool generated = false;
|
||||||
|
__block bool error = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putError:@1];
|
||||||
|
[object description];
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
id<SDisposable> disposable = [signal startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
generated = true;
|
||||||
|
} error:^(__unused id value)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
} completed:nil];
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
XCTAssertTrue(generated);
|
||||||
|
XCTAssertTrue(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMap
|
||||||
|
{
|
||||||
|
bool deallocated = false;
|
||||||
|
__block bool disposed = false;
|
||||||
|
__block bool generated = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
DeallocatingObject *object = [[DeallocatingObject alloc] initWithDeallocated:&deallocated];
|
||||||
|
SSignal *signal = [[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[object description];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
disposed = true;
|
||||||
|
}];
|
||||||
|
}] map:^id(id value)
|
||||||
|
{
|
||||||
|
[object description];
|
||||||
|
return @([value intValue] * 2);
|
||||||
|
}];
|
||||||
|
|
||||||
|
id<SDisposable> disposable = [signal startWithNext:^(id value)
|
||||||
|
{
|
||||||
|
generated = [value isEqual:@2];
|
||||||
|
} error:nil completed:nil];
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated);
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
XCTAssertTrue(generated);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSubscriberDisposal
|
||||||
|
{
|
||||||
|
__block bool disposed = false;
|
||||||
|
__block bool generated = false;
|
||||||
|
|
||||||
|
dispatch_queue_t queue = dispatch_queue_create(NULL, 0);
|
||||||
|
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(200);
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposed = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
id<SDisposable> disposable = [signal startWithNext:^(id value)
|
||||||
|
{
|
||||||
|
generated = true;
|
||||||
|
} error:nil completed:nil];
|
||||||
|
NSLog(@"dispose");
|
||||||
|
[disposable dispose];
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_barrier_sync(queue, ^
|
||||||
|
{
|
||||||
|
});
|
||||||
|
|
||||||
|
XCTAssertTrue(disposed);
|
||||||
|
XCTAssertFalse(generated);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testThen
|
||||||
|
{
|
||||||
|
__block bool generatedFirst = false;
|
||||||
|
__block bool disposedFirst = false;
|
||||||
|
__block bool generatedSecond = false;
|
||||||
|
__block bool disposedSecond = false;
|
||||||
|
__block int result = 0;
|
||||||
|
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
generatedFirst = true;
|
||||||
|
[subscriber putNext:@(1)];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedFirst = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
signal = [signal then:[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
generatedSecond = true;
|
||||||
|
[subscriber putNext:@(2)];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedSecond = true;
|
||||||
|
}];
|
||||||
|
}]];
|
||||||
|
|
||||||
|
[signal startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue(generatedFirst);
|
||||||
|
XCTAssertTrue(disposedFirst);
|
||||||
|
XCTAssertTrue(generatedSecond);
|
||||||
|
XCTAssertTrue(disposedSecond);
|
||||||
|
XCTAssert(result == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSwitchToLatest
|
||||||
|
{
|
||||||
|
__block int result = 0;
|
||||||
|
__block bool disposedOne = false;
|
||||||
|
__block bool disposedTwo = false;
|
||||||
|
__block bool disposedThree = false;
|
||||||
|
__block bool completedAll = false;
|
||||||
|
|
||||||
|
bool deallocatedOne = false;
|
||||||
|
bool deallocatedTwo = false;
|
||||||
|
bool deallocatedThree = false;
|
||||||
|
|
||||||
|
@autoreleasepool
|
||||||
|
{
|
||||||
|
DeallocatingObject *objectOne = [[DeallocatingObject alloc] initWithDeallocated:&deallocatedOne];
|
||||||
|
DeallocatingObject *objectTwo = [[DeallocatingObject alloc] initWithDeallocated:&deallocatedTwo];
|
||||||
|
DeallocatingObject *objectThree = [[DeallocatingObject alloc] initWithDeallocated:&deallocatedThree];
|
||||||
|
|
||||||
|
SSignal *one = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@(1)];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
__unused id a0 = [objectOne description];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
__unused id a0 = [objectOne description];
|
||||||
|
disposedOne = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
SSignal *two = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@(2)];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
__unused id a1 = [objectTwo description];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
__unused id a1 = [objectOne description];
|
||||||
|
disposedTwo = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
SSignal *three = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@(3)];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
__unused id a0 = [objectThree description];
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
__unused id a1 = [objectOne description];
|
||||||
|
disposedThree = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *signal = [[[[SSignal single:one] then:[SSignal single:two]] then:[SSignal single:three]] switchToLatest];
|
||||||
|
[signal startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
} error:nil completed:^
|
||||||
|
{
|
||||||
|
completedAll = true;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(result == 6);
|
||||||
|
XCTAssertTrue(disposedOne);
|
||||||
|
XCTAssertTrue(disposedTwo);
|
||||||
|
XCTAssertTrue(disposedThree);
|
||||||
|
XCTAssertTrue(deallocatedOne);
|
||||||
|
XCTAssertTrue(deallocatedTwo);
|
||||||
|
XCTAssertTrue(deallocatedThree);
|
||||||
|
XCTAssertTrue(completedAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSwitchToLatestError
|
||||||
|
{
|
||||||
|
__block bool errorGenerated = false;
|
||||||
|
|
||||||
|
SSignal *one = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putError:nil];
|
||||||
|
return nil;
|
||||||
|
}];
|
||||||
|
|
||||||
|
[one startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
|
||||||
|
} error:^(__unused id error)
|
||||||
|
{
|
||||||
|
errorGenerated = true;
|
||||||
|
} completed:^
|
||||||
|
{
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue(errorGenerated);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testSwitchToLatestCompleted
|
||||||
|
{
|
||||||
|
__block bool completedAll = false;
|
||||||
|
|
||||||
|
SSignal *one = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putCompletion];
|
||||||
|
return nil;
|
||||||
|
}];
|
||||||
|
|
||||||
|
[one startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
|
||||||
|
} error:^(__unused id error)
|
||||||
|
{
|
||||||
|
} completed:^
|
||||||
|
{
|
||||||
|
completedAll = true;
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue(completedAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testQueue
|
||||||
|
{
|
||||||
|
dispatch_queue_t queue = dispatch_queue_create(NULL, 0);
|
||||||
|
|
||||||
|
__block bool disposedFirst = false;
|
||||||
|
__block bool disposedSecond = false;
|
||||||
|
__block bool disposedThird = false;
|
||||||
|
__block int result = 0;
|
||||||
|
|
||||||
|
SSignal *firstSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedFirst = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *secondSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@2];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedSecond = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *thirdSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@3];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedThird = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *signal = [[[[SSignal single:firstSignal] then:[SSignal single:secondSignal]] then:[SSignal single:thirdSignal]] queue];
|
||||||
|
[signal startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
|
||||||
|
XCTAssertEqual(result, 6);
|
||||||
|
XCTAssertTrue(disposedFirst);
|
||||||
|
XCTAssertTrue(disposedSecond);
|
||||||
|
XCTAssertTrue(disposedThird);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testQueueInterrupted
|
||||||
|
{
|
||||||
|
dispatch_queue_t queue = dispatch_queue_create(NULL, 0);
|
||||||
|
|
||||||
|
__block bool disposedFirst = false;
|
||||||
|
__block bool disposedSecond = false;
|
||||||
|
__block bool disposedThird = false;
|
||||||
|
__block bool startedThird = false;
|
||||||
|
__block int result = 0;
|
||||||
|
|
||||||
|
SSignal *firstSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedFirst = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *secondSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@2];
|
||||||
|
[subscriber putError:nil];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedSecond = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *thirdSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
startedThird = true;
|
||||||
|
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@3];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedThird = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *signal = [[[[SSignal single:firstSignal] then:[SSignal single:secondSignal]] then:[SSignal single:thirdSignal]] queue];
|
||||||
|
[signal startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
|
||||||
|
XCTAssertEqual(result, 3);
|
||||||
|
XCTAssertTrue(disposedFirst);
|
||||||
|
XCTAssertTrue(disposedSecond);
|
||||||
|
XCTAssertFalse(startedThird);
|
||||||
|
XCTAssertFalse(disposedThird);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testQueueDisposed
|
||||||
|
{
|
||||||
|
dispatch_queue_t queue = dispatch_queue_create(NULL, 0);
|
||||||
|
|
||||||
|
__block bool disposedFirst = false;
|
||||||
|
__block bool disposedSecond = false;
|
||||||
|
__block bool disposedThird = false;
|
||||||
|
__block bool startedFirst = false;
|
||||||
|
__block bool startedSecond = false;
|
||||||
|
__block bool startedThird = false;
|
||||||
|
__block int result = 0;
|
||||||
|
|
||||||
|
SSignal *firstSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
startedFirst = true;
|
||||||
|
|
||||||
|
__block bool cancelled = false;
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
if (!cancelled)
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
cancelled = true;
|
||||||
|
disposedFirst = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *secondSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
startedSecond = true;
|
||||||
|
|
||||||
|
__block bool cancelled = false;
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
if (!cancelled)
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@2];
|
||||||
|
[subscriber putError:nil];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
cancelled = true;
|
||||||
|
disposedSecond = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *thirdSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
startedThird = true;
|
||||||
|
|
||||||
|
dispatch_async(queue, ^
|
||||||
|
{
|
||||||
|
usleep(100);
|
||||||
|
[subscriber putNext:@3];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
});
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
disposedThird = true;
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
SSignal *signal = [[[[SSignal single:firstSignal] then:[SSignal single:secondSignal]] then:[SSignal single:thirdSignal]] queue];
|
||||||
|
[[signal startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
}] dispose];
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
|
||||||
|
XCTAssertEqual(result, 0);
|
||||||
|
XCTAssertTrue(disposedFirst);
|
||||||
|
XCTAssertFalse(disposedSecond);
|
||||||
|
XCTAssertFalse(disposedThird);
|
||||||
|
|
||||||
|
XCTAssertTrue(startedFirst);
|
||||||
|
XCTAssertFalse(startedSecond);
|
||||||
|
XCTAssertFalse(startedThird);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRestart
|
||||||
|
{
|
||||||
|
SSignal *signal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[[SQueue concurrentDefaultQueue] dispatch:^
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
{
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
__block int result = 0;
|
||||||
|
|
||||||
|
[[[signal restart] take:3] startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result += [next intValue];
|
||||||
|
} error:^(id error) {
|
||||||
|
|
||||||
|
} completed:^{
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
usleep(100 * 1000);
|
||||||
|
|
||||||
|
XCTAssertEqual(result, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPipe
|
||||||
|
{
|
||||||
|
SPipe *pipe = [[SPipe alloc] init];
|
||||||
|
|
||||||
|
__block int result1 = 0;
|
||||||
|
id<SDisposable> disposable1 = [pipe.signalProducer() startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result1 += [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
__block int result2 = 0;
|
||||||
|
id<SDisposable> disposable2 = [pipe.signalProducer() startWithNext:^(id next)
|
||||||
|
{
|
||||||
|
result2 += [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
pipe.sink(@1);
|
||||||
|
|
||||||
|
XCTAssertEqual(result1, 1);
|
||||||
|
XCTAssertEqual(result2, 1);
|
||||||
|
|
||||||
|
[disposable1 dispose];
|
||||||
|
|
||||||
|
pipe.sink(@1);
|
||||||
|
|
||||||
|
XCTAssertEqual(result1, 1);
|
||||||
|
XCTAssertEqual(result2, 2);
|
||||||
|
|
||||||
|
[disposable2 dispose];
|
||||||
|
|
||||||
|
pipe.sink(@1);
|
||||||
|
|
||||||
|
XCTAssertEqual(result1, 1);
|
||||||
|
XCTAssertEqual(result2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDisposableDeadlock {
|
||||||
|
@autoreleasepool {
|
||||||
|
DisposableHolder *holder = [[DisposableHolder alloc] init];
|
||||||
|
holder = nil;
|
||||||
|
sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRetryIfNoError {
|
||||||
|
SSignal *s = [[SSignal single:@1] retryIf:^bool(__unused id error) {
|
||||||
|
return true;
|
||||||
|
}];
|
||||||
|
[s startWithNext:^(id next) {
|
||||||
|
XCTAssertEqual(next, @1);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRetryErrorNoMatch {
|
||||||
|
SSignal *s = [[SSignal fail:@false] retryIf:^bool(id error) {
|
||||||
|
return false;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRetryErrorMatch {
|
||||||
|
__block counter = 1;
|
||||||
|
SSignal *s = [[[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber) {
|
||||||
|
if (counter == 1) {
|
||||||
|
counter++;
|
||||||
|
[subscriber putError:@true];
|
||||||
|
} else {
|
||||||
|
[subscriber putNext:@(counter)];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}] retryIf:^bool(id error) {
|
||||||
|
return [error boolValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
__block int value = 0;
|
||||||
|
[s startWithNext:^(id next) {
|
||||||
|
value = [next intValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertEqual(value, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRetryErrorFailNoMatch {
|
||||||
|
__block counter = 1;
|
||||||
|
SSignal *s = [[[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber) {
|
||||||
|
if (counter == 1) {
|
||||||
|
counter++;
|
||||||
|
[subscriber putError:@true];
|
||||||
|
} else {
|
||||||
|
[subscriber putError:@false];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}] retryIf:^bool(id error) {
|
||||||
|
return [error boolValue];
|
||||||
|
}];
|
||||||
|
|
||||||
|
__block bool errorMatches = false;
|
||||||
|
[s startWithNext:nil error:^(id error) {
|
||||||
|
errorMatches = ![error boolValue];
|
||||||
|
} completed:nil];
|
||||||
|
|
||||||
|
XCTAssert(errorMatches);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#else
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#endif
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
@import SSignalKit;
|
||||||
|
|
||||||
|
@interface SSignalPerformanceTests : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SSignalPerformanceTests
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
[super tearDown];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMap
|
||||||
|
{
|
||||||
|
[self measureBlock:^
|
||||||
|
{
|
||||||
|
SSignal *signal = [[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
{
|
||||||
|
[subscriber putNext:@1];
|
||||||
|
[subscriber putCompletion];
|
||||||
|
return nil;
|
||||||
|
}] map:^id (id value)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (int i = 0; i < 100000; i++)
|
||||||
|
{
|
||||||
|
[signal startWithNext:^(__unused id next)
|
||||||
|
{
|
||||||
|
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
2348
submodules/SSignalKit/SSignalKit_Xcode.xcodeproj/project.pbxproj
Normal file
2348
submodules/SSignalKit/SSignalKit_Xcode.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
7
submodules/SSignalKit/SSignalKit_Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
submodules/SSignalKit/SSignalKit_Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:SSignalKit.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
26
submodules/SSignalKit/SwiftSignalKit copy-Info.plist
Normal file
26
submodules/SSignalKit/SwiftSignalKit copy-Info.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
43
submodules/SSignalKit/SwiftSignalKit/Atomic.swift
Normal file
43
submodules/SSignalKit/SwiftSignalKit/Atomic.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Atomic<T> {
|
||||||
|
private var lock: pthread_mutex_t
|
||||||
|
private var value: T
|
||||||
|
|
||||||
|
public init(value: T) {
|
||||||
|
self.lock = pthread_mutex_t()
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func with<R>(_ f: (T) -> R) -> R {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
let result = f(self.value)
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func modify(_ f: (T) -> T) -> T {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
let result = f(self.value)
|
||||||
|
self.value = result
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func swap(_ value: T) -> T {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
let previous = self.value
|
||||||
|
self.value = value
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
return previous
|
||||||
|
}
|
||||||
|
}
|
||||||
97
submodules/SSignalKit/SwiftSignalKit/Bag.swift
Normal file
97
submodules/SSignalKit/SwiftSignalKit/Bag.swift
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Bag<T> {
|
||||||
|
public typealias Index = Int
|
||||||
|
private var nextIndex: Index = 0
|
||||||
|
private var items: [T] = []
|
||||||
|
private var itemKeys: [Index] = []
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func add(_ item: T) -> Index {
|
||||||
|
let key = self.nextIndex
|
||||||
|
self.nextIndex += 1
|
||||||
|
self.items.append(item)
|
||||||
|
self.itemKeys.append(key)
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
public func get(_ index: Index) -> T? {
|
||||||
|
var i = 0
|
||||||
|
for key in self.itemKeys {
|
||||||
|
if key == index {
|
||||||
|
return self.items[i]
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(_ index: Index) {
|
||||||
|
var i = 0
|
||||||
|
for key in self.itemKeys {
|
||||||
|
if key == index {
|
||||||
|
self.items.remove(at: i)
|
||||||
|
self.itemKeys.remove(at: i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeAll() {
|
||||||
|
self.items.removeAll()
|
||||||
|
self.itemKeys.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func copyItems() -> [T] {
|
||||||
|
return self.items
|
||||||
|
}
|
||||||
|
|
||||||
|
public func copyItemsWithIndices() -> [(Index, T)] {
|
||||||
|
var result: [(Index, T)] = []
|
||||||
|
var i = 0
|
||||||
|
for key in self.itemKeys {
|
||||||
|
result.append((key, self.items[i]))
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isEmpty: Bool {
|
||||||
|
return self.items.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public var first: (Index, T)? {
|
||||||
|
if !self.items.isEmpty {
|
||||||
|
return (self.itemKeys[0], self.items[0])
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class CounterBag {
|
||||||
|
private var nextIndex: Int = 1
|
||||||
|
private var items = Set<Int>()
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func add() -> Int {
|
||||||
|
let index = self.nextIndex
|
||||||
|
self.nextIndex += 1
|
||||||
|
self.items.insert(index)
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(_ index: Int) {
|
||||||
|
self.items.remove(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isEmpty: Bool {
|
||||||
|
return self.items.isEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
233
submodules/SSignalKit/SwiftSignalKit/Disposable.swift
Normal file
233
submodules/SSignalKit/SwiftSignalKit/Disposable.swift
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol Disposable: class {
|
||||||
|
func dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _EmptyDisposable: Disposable {
|
||||||
|
func dispose() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let EmptyDisposable: Disposable = _EmptyDisposable()
|
||||||
|
|
||||||
|
public final class ActionDisposable : Disposable {
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
|
||||||
|
private var action: (() -> Void)?
|
||||||
|
|
||||||
|
public init(action: @escaping() -> Void) {
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
var freeAction: (() -> Void)?
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
freeAction = self.action
|
||||||
|
self.action = nil
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if let freeAction = freeAction {
|
||||||
|
withExtendedLifetime(freeAction, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
let disposeAction: (() -> Void)?
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
disposeAction = self.action
|
||||||
|
self.action = nil
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
disposeAction?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class MetaDisposable : Disposable {
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private var disposed = false
|
||||||
|
private var disposable: Disposable! = nil
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
var freeDisposable: Disposable?
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if let disposable = self.disposable {
|
||||||
|
freeDisposable = disposable
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
if let freeDisposable = freeDisposable {
|
||||||
|
withExtendedLifetime(freeDisposable, { })
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(_ disposable: Disposable?) {
|
||||||
|
var previousDisposable: Disposable! = nil
|
||||||
|
var disposeImmediately = false
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
disposeImmediately = self.disposed
|
||||||
|
if !disposeImmediately {
|
||||||
|
previousDisposable = self.disposable
|
||||||
|
if let disposable = disposable {
|
||||||
|
self.disposable = disposable
|
||||||
|
} else {
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if previousDisposable != nil {
|
||||||
|
previousDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
if disposeImmediately {
|
||||||
|
if let disposable = disposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose()
|
||||||
|
{
|
||||||
|
var disposable: Disposable! = nil
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.disposed {
|
||||||
|
self.disposed = true
|
||||||
|
disposable = self.disposable
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if disposable != nil {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DisposableSet : Disposable {
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private var disposed = false
|
||||||
|
private var disposables: [Disposable] = []
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.disposables.removeAll()
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func add(_ disposable: Disposable) {
|
||||||
|
var disposeImmediately = false
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if self.disposed {
|
||||||
|
disposeImmediately = true
|
||||||
|
} else {
|
||||||
|
self.disposables.append(disposable)
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if disposeImmediately {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(_ disposable: Disposable) {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if let index = self.disposables.index(where: { $0 === disposable }) {
|
||||||
|
self.disposables.remove(at: index)
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
var disposables: [Disposable] = []
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.disposed {
|
||||||
|
self.disposed = true
|
||||||
|
disposables = self.disposables
|
||||||
|
self.disposables = []
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if disposables.count != 0 {
|
||||||
|
for disposable in disposables {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DisposableDict<T: Hashable> : Disposable {
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private var disposed = false
|
||||||
|
private var disposables: [T: Disposable] = [:]
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.disposables.removeAll()
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(_ disposable: Disposable?, forKey key: T) {
|
||||||
|
var disposeImmediately = false
|
||||||
|
var disposePrevious: Disposable?
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if self.disposed {
|
||||||
|
disposeImmediately = true
|
||||||
|
} else {
|
||||||
|
disposePrevious = self.disposables[key]
|
||||||
|
if let disposable = disposable {
|
||||||
|
self.disposables[key] = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if disposeImmediately {
|
||||||
|
disposable?.dispose()
|
||||||
|
}
|
||||||
|
disposePrevious?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
var disposables: [T: Disposable] = [:]
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.disposed {
|
||||||
|
self.disposed = true
|
||||||
|
disposables = self.disposables
|
||||||
|
self.disposables = [:]
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if disposables.count != 0 {
|
||||||
|
for disposable in disposables.values {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
submodules/SSignalKit/SwiftSignalKit/Info.plist
Normal file
26
submodules/SSignalKit/SwiftSignalKit/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
19
submodules/SSignalKit/SwiftSignalKit/Lock.swift
Normal file
19
submodules/SSignalKit/SwiftSignalKit/Lock.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Lock {
|
||||||
|
private var mutex = pthread_mutex_t()
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
pthread_mutex_init(&self.mutex, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.mutex)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func locked(_ f: () -> ()) {
|
||||||
|
pthread_mutex_lock(&self.mutex)
|
||||||
|
f()
|
||||||
|
pthread_mutex_unlock(&self.mutex)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
submodules/SSignalKit/SwiftSignalKit/Multicast.swift
Normal file
88
submodules/SSignalKit/SwiftSignalKit/Multicast.swift
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private final class MulticastInstance<T> {
|
||||||
|
let disposable: Disposable
|
||||||
|
var subscribers = Bag<(T) -> Void>()
|
||||||
|
var lock = Lock()
|
||||||
|
|
||||||
|
init(disposable: Disposable) {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Multicast<T> {
|
||||||
|
private let lock = Lock()
|
||||||
|
private var instances: [String: MulticastInstance<T>] = [:]
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func get(key: String, signal: Signal<T, NoError>) -> Signal<T, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
var instance: MulticastInstance<T>!
|
||||||
|
var beginDisposable: MetaDisposable?
|
||||||
|
self.lock.locked {
|
||||||
|
if let existing = self.instances[key] {
|
||||||
|
instance = existing
|
||||||
|
} else {
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
instance = MulticastInstance(disposable: disposable)
|
||||||
|
beginDisposable = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var index: Bag<(T) -> Void>.Index!
|
||||||
|
instance.lock.locked {
|
||||||
|
index = instance.subscribers.add({ next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if let beginDisposable = beginDisposable {
|
||||||
|
beginDisposable.set(signal.start(next: { next in
|
||||||
|
var subscribers: [(T) -> Void]!
|
||||||
|
instance.lock.locked {
|
||||||
|
subscribers = instance.subscribers.copyItems()
|
||||||
|
}
|
||||||
|
for subscriber in subscribers {
|
||||||
|
subscriber(next)
|
||||||
|
}
|
||||||
|
}, error: { _ in
|
||||||
|
self.lock.locked {
|
||||||
|
let _ = self.instances.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
self.lock.locked {
|
||||||
|
self.instances.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
var remove = false
|
||||||
|
instance.lock.locked {
|
||||||
|
instance.subscribers.remove(index)
|
||||||
|
if instance.subscribers.isEmpty {
|
||||||
|
remove = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if remove {
|
||||||
|
self.lock.locked {
|
||||||
|
let _ = self.instances.removeValue(forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class MulticastPromise<T> {
|
||||||
|
public let subscribers = Bag<(T) -> Void>()
|
||||||
|
public let lock = Lock()
|
||||||
|
public var value: T?
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
139
submodules/SSignalKit/SwiftSignalKit/Promise.swift
Normal file
139
submodules/SSignalKit/SwiftSignalKit/Promise.swift
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Promise<T> {
|
||||||
|
private var initializeOnFirstAccess: Signal<T, NoError>?
|
||||||
|
private var value: T?
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private let disposable = MetaDisposable()
|
||||||
|
private let subscribers = Bag<(T) -> Void>()
|
||||||
|
|
||||||
|
public var onDeinit: (() -> Void)?
|
||||||
|
|
||||||
|
public init(initializeOnFirstAccess: Signal<T, NoError>?) {
|
||||||
|
self.initializeOnFirstAccess = initializeOnFirstAccess
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ value: T) {
|
||||||
|
self.value = value
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.onDeinit?()
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
self.disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(_ signal: Signal<T, NoError>) {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.value = nil
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
self.disposable.set(signal.start(next: { [weak self] next in
|
||||||
|
if let strongSelf = self {
|
||||||
|
pthread_mutex_lock(&strongSelf.lock)
|
||||||
|
strongSelf.value = next
|
||||||
|
let subscribers = strongSelf.subscribers.copyItems()
|
||||||
|
pthread_mutex_unlock(&strongSelf.lock)
|
||||||
|
|
||||||
|
for subscriber in subscribers {
|
||||||
|
subscriber(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func get() -> Signal<T, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
var initializeOnFirstAccessNow: Signal<T, NoError>?
|
||||||
|
if let initializeOnFirstAccess = self.initializeOnFirstAccess {
|
||||||
|
initializeOnFirstAccessNow = initializeOnFirstAccess
|
||||||
|
self.initializeOnFirstAccess = nil
|
||||||
|
}
|
||||||
|
let currentValue = self.value
|
||||||
|
let index = self.subscribers.add({ next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
})
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if let currentValue = currentValue {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let initializeOnFirstAccessNow = initializeOnFirstAccessNow {
|
||||||
|
self.set(initializeOnFirstAccessNow)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.subscribers.remove(index)
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ValuePromise<T: Equatable> {
|
||||||
|
private var value: T?
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private let subscribers = Bag<(T) -> Void>()
|
||||||
|
public let ignoreRepeated: Bool
|
||||||
|
|
||||||
|
public init(_ value: T, ignoreRepeated: Bool = false) {
|
||||||
|
self.value = value
|
||||||
|
self.ignoreRepeated = ignoreRepeated
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(ignoreRepeated: Bool = false) {
|
||||||
|
self.ignoreRepeated = ignoreRepeated
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(_ value: T) {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
let subscribers: [(T) -> Void]
|
||||||
|
if !self.ignoreRepeated || self.value != value {
|
||||||
|
self.value = value
|
||||||
|
subscribers = self.subscribers.copyItems()
|
||||||
|
} else {
|
||||||
|
subscribers = []
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock);
|
||||||
|
|
||||||
|
for subscriber in subscribers {
|
||||||
|
subscriber(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func get() -> Signal<T, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
let currentValue = self.value
|
||||||
|
let index = self.subscribers.add({ next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
})
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if let currentValue = currentValue {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.subscribers.remove(index)
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
submodules/SSignalKit/SwiftSignalKit/Queue.swift
Normal file
88
submodules/SSignalKit/SwiftSignalKit/Queue.swift
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private let QueueSpecificKey = DispatchSpecificKey<NSObject>()
|
||||||
|
|
||||||
|
private let globalMainQueue = Queue(queue: DispatchQueue.main, specialIsMainQueue: true)
|
||||||
|
private let globalDefaultQueue = Queue(queue: DispatchQueue.global(qos: .default), specialIsMainQueue: false)
|
||||||
|
private let globalBackgroundQueue = Queue(queue: DispatchQueue.global(qos: .background), specialIsMainQueue: false)
|
||||||
|
|
||||||
|
public final class Queue {
|
||||||
|
private let nativeQueue: DispatchQueue
|
||||||
|
private var specific = NSObject()
|
||||||
|
private let specialIsMainQueue: Bool
|
||||||
|
|
||||||
|
public var queue: DispatchQueue {
|
||||||
|
get {
|
||||||
|
return self.nativeQueue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func mainQueue() -> Queue {
|
||||||
|
return globalMainQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func concurrentDefaultQueue() -> Queue {
|
||||||
|
return globalDefaultQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func concurrentBackgroundQueue() -> Queue {
|
||||||
|
return globalBackgroundQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(queue: DispatchQueue) {
|
||||||
|
self.nativeQueue = queue
|
||||||
|
self.specialIsMainQueue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate init(queue: DispatchQueue, specialIsMainQueue: Bool) {
|
||||||
|
self.nativeQueue = queue
|
||||||
|
self.specialIsMainQueue = specialIsMainQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(name: String? = nil, qos: DispatchQoS = .default) {
|
||||||
|
self.nativeQueue = DispatchQueue(label: name ?? "", qos: qos)
|
||||||
|
|
||||||
|
self.specialIsMainQueue = false
|
||||||
|
|
||||||
|
self.nativeQueue.setSpecific(key: QueueSpecificKey, value: self.specific)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isCurrent() -> Bool {
|
||||||
|
if DispatchQueue.getSpecific(key: QueueSpecificKey) === self.specific {
|
||||||
|
return true
|
||||||
|
} else if self.specialIsMainQueue && Thread.isMainThread {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func async(_ f: @escaping () -> Void) {
|
||||||
|
if self.isCurrent() {
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
self.nativeQueue.async(execute: f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func sync(_ f: () -> Void) {
|
||||||
|
if self.isCurrent() {
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
self.nativeQueue.sync(execute: f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func justDispatch(_ f: @escaping () -> Void) {
|
||||||
|
self.nativeQueue.async(execute: f)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func justDispatchWithQoS(qos: DispatchQoS, _ f: @escaping () -> Void) {
|
||||||
|
self.nativeQueue.async(group: nil, qos: qos, flags: [.enforceQoS], execute: f)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func after(_ delay: Double, _ f: @escaping() -> Void) {
|
||||||
|
let time: DispatchTime = DispatchTime.now() + delay
|
||||||
|
self.nativeQueue.asyncAfter(deadline: time, execute: f)
|
||||||
|
}
|
||||||
|
}
|
||||||
53
submodules/SSignalKit/SwiftSignalKit/QueueLocalObject.swift
Normal file
53
submodules/SSignalKit/SwiftSignalKit/QueueLocalObject.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class QueueLocalObject<T: AnyObject> {
|
||||||
|
private let queue: Queue
|
||||||
|
private var valueRef: Unmanaged<T>?
|
||||||
|
|
||||||
|
public init(queue: Queue, generate: @escaping () -> T) {
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
|
self.queue.async {
|
||||||
|
let value = generate()
|
||||||
|
self.valueRef = Unmanaged.passRetained(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
let valueRef = self.valueRef
|
||||||
|
self.queue.async {
|
||||||
|
valueRef?.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func with(_ f: @escaping (T) -> Void) {
|
||||||
|
self.queue.async {
|
||||||
|
if let valueRef = self.valueRef {
|
||||||
|
let value = valueRef.takeUnretainedValue()
|
||||||
|
f(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func syncWith<R>(_ f: @escaping (T) -> R) -> R? {
|
||||||
|
var result: R?
|
||||||
|
self.queue.sync {
|
||||||
|
if let valueRef = self.valueRef {
|
||||||
|
let value = valueRef.takeUnretainedValue()
|
||||||
|
result = f(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func signalWith<R, E>(_ f: @escaping (T, Subscriber<R, E>) -> Disposable) -> Signal<R, E> {
|
||||||
|
return Signal { [weak self] subscriber in
|
||||||
|
if let strongSelf = self, let valueRef = strongSelf.valueRef {
|
||||||
|
let value = valueRef.takeUnretainedValue()
|
||||||
|
return f(value, subscriber)
|
||||||
|
} else {
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
} |> runOn(self.queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
85
submodules/SSignalKit/SwiftSignalKit/Signal.swift
Normal file
85
submodules/SSignalKit/SwiftSignalKit/Signal.swift
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
let doNothing: () -> Void = { }
|
||||||
|
|
||||||
|
public enum NoValue {
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NoError {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func identity<A>(a: A) -> A {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
precedencegroup PipeRight {
|
||||||
|
associativity: left
|
||||||
|
higherThan: DefaultPrecedence
|
||||||
|
}
|
||||||
|
|
||||||
|
infix operator |> : PipeRight
|
||||||
|
|
||||||
|
public func |> <T, U>(value: T, function: ((T) -> U)) -> U {
|
||||||
|
return function(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class SubscriberDisposable<T, E> : Disposable {
|
||||||
|
private let subscriber: Subscriber<T, E>
|
||||||
|
private let disposable: Disposable
|
||||||
|
|
||||||
|
init(subscriber: Subscriber<T, E>, disposable: Disposable) {
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
subscriber.markTerminatedWithoutDisposal()
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Signal<T, E> {
|
||||||
|
private let generator: (Subscriber<T, E>) -> Disposable
|
||||||
|
|
||||||
|
public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) {
|
||||||
|
self.generator = generator
|
||||||
|
}
|
||||||
|
|
||||||
|
public func start(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
|
||||||
|
let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
|
||||||
|
let disposable = self.generator(subscriber)
|
||||||
|
subscriber.assignDisposable(disposable)
|
||||||
|
return SubscriberDisposable(subscriber: subscriber, disposable: disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func single(_ value: T) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func complete() -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fail(_ error: E) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putError(error)
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func never() -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { _ in
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
168
submodules/SSignalKit/SwiftSignalKit/Signal_Catch.swift
Normal file
168
submodules/SSignalKit/SwiftSignalKit/Signal_Catch.swift
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func `catch`<T, E, R>(_ f: @escaping(E) -> Signal<T, R>) -> (Signal<T, E>) -> Signal<T, R> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, R> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
let anotherSignal = f(error)
|
||||||
|
|
||||||
|
disposable.add(anotherSignal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func recursiveFunction(_ f: @escaping(@escaping() -> Void) -> Void) -> (() -> Void) {
|
||||||
|
return {
|
||||||
|
f(recursiveFunction(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func restart<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let shouldRestart = Atomic(value: true)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRestart = shouldRestart.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRestart {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
recurse()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
let _ = shouldRestart.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func recurse<T, E>(_ latestValue: T?) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let shouldRestart = Atomic(value: true)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRestart = shouldRestart.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRestart {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
recurse()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
let _ = shouldRestart.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func retry<T, E>(_ delayIncrement: Double, maxDelay: Double, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, NoError> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let shouldRetry = Atomic(value: true)
|
||||||
|
let currentDelay = Atomic(value: 0.0)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRetry = shouldRetry.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRetry {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
let delay = currentDelay.modify { value in
|
||||||
|
return min(maxDelay, value + delayIncrement)
|
||||||
|
}
|
||||||
|
|
||||||
|
let time: DispatchTime = DispatchTime.now() + Double(delay)
|
||||||
|
queue.queue.asyncAfter(deadline: time, execute: {
|
||||||
|
recurse()
|
||||||
|
})
|
||||||
|
}, completed: {
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func restartIfError<T, E>(_ signal: Signal<T, E>) -> Signal<T, NoError> {
|
||||||
|
return Signal<T, NoError> { subscriber in
|
||||||
|
let shouldRetry = Atomic(value: true)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRetry = shouldRetry.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRetry {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
recurse()
|
||||||
|
}, completed: {
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
163
submodules/SSignalKit/SwiftSignalKit/Signal_Combine.swift
Normal file
163
submodules/SSignalKit/SwiftSignalKit/Signal_Combine.swift
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private struct SignalCombineState {
|
||||||
|
let values: [Int : Any]
|
||||||
|
let completed: Set<Int>
|
||||||
|
let error: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
private func combineLatestAny<E, R>(_ signals: [Signal<Any, E>], combine: @escaping([Any]) -> R, initialValues: [Int : Any], queue: Queue?) -> Signal<R, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = Atomic(value: SignalCombineState(values: initialValues, completed: Set(), error: false))
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
if initialValues.count == signals.count {
|
||||||
|
var values: [Any] = []
|
||||||
|
for i in 0 ..< initialValues.count {
|
||||||
|
values.append(initialValues[i]!)
|
||||||
|
}
|
||||||
|
subscriber.putNext(combine(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = signals.count
|
||||||
|
for iterationIndex in 0 ..< count {
|
||||||
|
let index = iterationIndex
|
||||||
|
var signal = signals[index]
|
||||||
|
if let queue = queue {
|
||||||
|
signal = signal
|
||||||
|
|> deliverOn(queue)
|
||||||
|
}
|
||||||
|
let signalDisposable = signal.start(next: { next in
|
||||||
|
let currentState = state.modify { current in
|
||||||
|
var values = current.values
|
||||||
|
values[index] = next
|
||||||
|
return SignalCombineState(values: values, completed: current.completed, error: current.error)
|
||||||
|
}
|
||||||
|
if currentState.values.count == count {
|
||||||
|
var values: [Any] = []
|
||||||
|
for i in 0 ..< count {
|
||||||
|
values.append(currentState.values[i]!)
|
||||||
|
}
|
||||||
|
subscriber.putNext(combine(values))
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
var emitError = false
|
||||||
|
let _ = state.modify { current in
|
||||||
|
if !current.error {
|
||||||
|
emitError = true
|
||||||
|
return SignalCombineState(values: current.values, completed: current.completed, error: true)
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if emitError {
|
||||||
|
subscriber.putError(error)
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
var emitCompleted = false
|
||||||
|
let _ = state.modify { current in
|
||||||
|
if !current.completed.contains(index) {
|
||||||
|
var completed = current.completed
|
||||||
|
completed.insert(index)
|
||||||
|
emitCompleted = completed.count == count
|
||||||
|
return SignalCombineState(values: current.values, completed: completed, error: current.error)
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
if emitCompleted {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
disposable.add(signalDisposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func signalOfAny<T, E>(_ signal: Signal<T, E>) -> Signal<Any, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>) -> Signal<(T1, T2), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ v1: T1, _ s2: Signal<T2, E>, _ v2: T2) -> Signal<(T1, T2), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2)
|
||||||
|
}, initialValues: [0: v1, 1: v2], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>) -> Signal<(T1, T2, T3), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3)], combine: { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2, values[2] as! T3)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>) -> Signal<(T1, T2, T3, T4), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4)], combine: { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>) -> Signal<(T1, T2, T3, T4, T5), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5)], combine: { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, T6, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>) -> Signal<(T1, T2, T3, T4, T5, T6), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6)], combine: { values in
|
||||||
|
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)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7)], combine: { values in
|
||||||
|
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)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8)], combine: { values in
|
||||||
|
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)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9)], combine: { values in
|
||||||
|
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)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), E> {
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> {
|
||||||
|
if signals.count == 0 {
|
||||||
|
return single([T](), E.self)
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineLatestAny(signals.map({signalOfAny($0)}), combine: { values in
|
||||||
|
var combined: [T] = []
|
||||||
|
for value in values {
|
||||||
|
combined.append(value as! T)
|
||||||
|
}
|
||||||
|
return combined
|
||||||
|
}, initialValues: [:], queue: queue)
|
||||||
|
}
|
||||||
122
submodules/SSignalKit/SwiftSignalKit/Signal_Dispatch.swift
Normal file
122
submodules/SSignalKit/SwiftSignalKit/Signal_Dispatch.swift
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func deliverOn<T, E>(_ queue: Queue) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
queue.async {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
queue.async {
|
||||||
|
subscriber.putError(error)
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
queue.async {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deliverOnMainQueue<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return signal |> deliverOn(Queue.mainQueue())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deliverOn<T, E>(_ threadPool: ThreadPool) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let queue = threadPool.nextQueue()
|
||||||
|
return signal.start(next: { next in
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled.with { $0 } {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, error: { error in
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled.with { $0 } {
|
||||||
|
subscriber.putError(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, completed: {
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled.with { $0 } {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func runOn<T, E>(_ queue: Queue) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
if queue.isCurrent() {
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
var cancelled = false
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
cancelled = true
|
||||||
|
})
|
||||||
|
|
||||||
|
queue.async {
|
||||||
|
if cancelled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func runOn<T, E>(_ threadPool: ThreadPool) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let cancelled = false
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
let task = ThreadPoolTask { state in
|
||||||
|
if cancelled || state.cancelled.with { $0 } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
task.cancel()
|
||||||
|
})
|
||||||
|
|
||||||
|
threadPool.addTask(task)
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
submodules/SSignalKit/SwiftSignalKit/Signal_Loop.swift
Normal file
46
submodules/SSignalKit/SwiftSignalKit/Signal_Loop.swift
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum SignalFeedbackLoopState<T> {
|
||||||
|
case initial
|
||||||
|
case loop(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func feedbackLoop<R1, R, E>(once: @escaping (SignalFeedbackLoopState<R1>) -> Signal<R1, E>?, reduce: @escaping (R1, R1) -> R1) -> Signal<R, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let state = Atomic<R1?>(value: nil)
|
||||||
|
|
||||||
|
var loopAgain: (() -> Void)?
|
||||||
|
|
||||||
|
let loopOnce: (MetaDisposable?) -> Void = { disposable in
|
||||||
|
if let signal = once(.initial) {
|
||||||
|
disposable?.set(signal.start(next: { next in
|
||||||
|
let _ = state.modify { value in
|
||||||
|
if let value = value {
|
||||||
|
return reduce(value, next)
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
loopAgain?()
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loopAgain = { [weak currentDisposable] in
|
||||||
|
loopOnce(currentDisposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
loopOnce(currentDisposable)
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
submodules/SSignalKit/SwiftSignalKit/Signal_Mapping.swift
Normal file
128
submodules/SSignalKit/SwiftSignalKit/Signal_Mapping.swift
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func map<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<R, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(f(next))
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func filter<T, E>(_ f: @escaping(T) -> Bool) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
if f(next) {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func flatMap<T, E, R>(_ f: @escaping (T) -> R?) -> (Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<R, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
if let value = f(next) {
|
||||||
|
subscriber.putNext(value)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapError<T, E, R>(_ f: @escaping(E) -> R) -> (Signal<T, E>) -> Signal<T, R> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, R> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(f(error))
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func introduceError<T, E>(_ type: E.Type) -> (Signal<T, NoError>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { _ in
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DistinctUntilChangedContext<T> {
|
||||||
|
var value: T?
|
||||||
|
}
|
||||||
|
|
||||||
|
public func distinctUntilChanged<T: Equatable, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let context = Atomic(value: DistinctUntilChangedContext<T>())
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let pass = context.with { context -> Bool in
|
||||||
|
if let value = context.value, value == next {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
context.value = next
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pass {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func distinctUntilChanged<T, E>(isEqual: @escaping (T, T) -> Bool) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let context = Atomic(value: DistinctUntilChangedContext<T>())
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let pass = context.with { context -> Bool in
|
||||||
|
if let value = context.value, isEqual(value, next) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
context.value = next
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pass {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum SignalEvent<T, E> {
|
||||||
|
case Next(T)
|
||||||
|
case Error(E)
|
||||||
|
case Completion
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dematerialize<T, E>(signal: Signal<T, E>) -> Signal<SignalEvent<T, E>, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(.Next(next))
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putNext(.Error(error))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(.Completion)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func materialize<T, E>(signal: Signal<SignalEvent<T, E>, NoError>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
switch next {
|
||||||
|
case let .Next(next):
|
||||||
|
subscriber.putNext(next)
|
||||||
|
case let .Error(error):
|
||||||
|
subscriber.putError(error)
|
||||||
|
case .Completion:
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}, error: { _ in
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
7
submodules/SSignalKit/SwiftSignalKit/Signal_Merge.swift
Normal file
7
submodules/SSignalKit/SwiftSignalKit/Signal_Merge.swift
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/*public func merge<T, E>(signal1: Signal<T, E>, signal2: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
|
||||||
|
}
|
||||||
|
}*/
|
||||||
248
submodules/SSignalKit/SwiftSignalKit/Signal_Meta.swift
Normal file
248
submodules/SSignalKit/SwiftSignalKit/Signal_Meta.swift
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private final class SignalQueueState<T, E>: Disposable {
|
||||||
|
var lock = pthread_mutex_t()
|
||||||
|
var executingSignal = false
|
||||||
|
var terminated = false
|
||||||
|
|
||||||
|
var disposable: Disposable = EmptyDisposable
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
var subscriber: Subscriber<T, E>?
|
||||||
|
|
||||||
|
var queuedSignals: [Signal<T, E>] = []
|
||||||
|
let queueMode: Bool
|
||||||
|
let throttleMode: Bool
|
||||||
|
|
||||||
|
init(subscriber: Subscriber<T, E>, queueMode: Bool, throttleMode: Bool) {
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.queueMode = queueMode
|
||||||
|
self.throttleMode = throttleMode
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginWithDisposable(_ disposable: Disposable) {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueSignal(_ signal: Signal<T, E>) {
|
||||||
|
var startSignal = false
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if self.queueMode && self.executingSignal {
|
||||||
|
if self.throttleMode {
|
||||||
|
self.queuedSignals.removeAll()
|
||||||
|
}
|
||||||
|
self.queuedSignals.append(signal)
|
||||||
|
} else {
|
||||||
|
self.executingSignal = true
|
||||||
|
startSignal = true
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if startSignal {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
assert(self.subscriber != nil)
|
||||||
|
self.subscriber?.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
assert(self.subscriber != nil)
|
||||||
|
self.subscriber?.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
self.headCompleted()
|
||||||
|
})
|
||||||
|
self.currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func headCompleted() {
|
||||||
|
while true {
|
||||||
|
let leftFunction = Atomic(value: false)
|
||||||
|
|
||||||
|
var nextSignal: Signal<T, E>! = nil
|
||||||
|
|
||||||
|
var terminated = false
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
self.executingSignal = false
|
||||||
|
if self.queueMode {
|
||||||
|
if self.queuedSignals.count != 0 {
|
||||||
|
nextSignal = self.queuedSignals[0]
|
||||||
|
self.queuedSignals.remove(at: 0)
|
||||||
|
self.executingSignal = true
|
||||||
|
} else {
|
||||||
|
terminated = self.terminated
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
terminated = self.terminated
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if terminated {
|
||||||
|
self.subscriber?.putCompletion()
|
||||||
|
} else if nextSignal != nil {
|
||||||
|
let disposable = nextSignal.start(next: { next in
|
||||||
|
assert(self.subscriber != nil)
|
||||||
|
self.subscriber?.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
assert(self.subscriber != nil)
|
||||||
|
self.subscriber?.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
if leftFunction.swap(true) == true {
|
||||||
|
self.headCompleted()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftFunction.swap(true) == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginCompletion() {
|
||||||
|
var executingSignal = false
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
executingSignal = self.executingSignal
|
||||||
|
self.terminated = true
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if !executingSignal {
|
||||||
|
self.subscriber?.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
self.currentDisposable.dispose()
|
||||||
|
self.disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func switchToLatest<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = SignalQueueState(subscriber: subscriber, queueMode: false, throttleMode: false)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueSignal(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func queue<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: false)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueSignal(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func throttled<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: true)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueSignal(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToSignal<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return { signal -> Signal<R, E> in
|
||||||
|
return Signal<Signal<R, E>, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(f(next))
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
} |> switchToLatest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ignoreValues<T, E>(_ signal: Signal<T, E>) -> Signal<Never, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToSignalPromotingError<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, NoError>) -> Signal<R, E> {
|
||||||
|
return { signal -> Signal<R, E> in
|
||||||
|
return Signal<Signal<R, E>, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(f(next))
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
} |> switchToLatest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToQueue<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return { signal -> Signal<R, E> in
|
||||||
|
return signal |> map { f($0) } |> queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToThrottled<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return { signal -> Signal<R, E> in
|
||||||
|
return signal |> map { f($0) } |> throttled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func then<T, E>(_ nextSignal: Signal<T, E>) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal -> Signal<T, E> in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
disposable.add(nextSignal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deferred<T, E>(_ generator: @escaping() -> Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return generator().start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
189
submodules/SSignalKit/SwiftSignalKit/Signal_Reduce.swift
Normal file
189
submodules/SSignalKit/SwiftSignalKit/Signal_Reduce.swift
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func reduceLeft<T, E>(value: T, f: @escaping(T, T) -> T) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
var currentValue = value
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
currentValue = f(currentValue, next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reduceLeft<T, E>(value: T, f: @escaping(T, T, (T) -> Void) -> T) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
var currentValue = value
|
||||||
|
let emit: (T) -> Void = { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
currentValue = f(currentValue, next, emit)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Passthrough<T> {
|
||||||
|
case None
|
||||||
|
case Some(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ReduceQueueState<T, E> : Disposable {
|
||||||
|
var lock: OSSpinLock = 0
|
||||||
|
var executingSignal = false
|
||||||
|
var terminated = false
|
||||||
|
|
||||||
|
var disposable: Disposable = EmptyDisposable
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
let subscriber: Subscriber<T, E>
|
||||||
|
|
||||||
|
var queuedValues: [T] = []
|
||||||
|
var generator: (T, T) -> Signal<(T, Passthrough<T>), E>
|
||||||
|
var value: T
|
||||||
|
|
||||||
|
init(subscriber: Subscriber<T, E>, value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough<T>), E>) {
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.generator = generator
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginWithDisposable(_ disposable: Disposable) {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueNext(_ next: T) {
|
||||||
|
var startSignal = false
|
||||||
|
var currentValue: T
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
currentValue = self.value
|
||||||
|
if self.executingSignal {
|
||||||
|
self.queuedValues.append(next)
|
||||||
|
} else {
|
||||||
|
self.executingSignal = true
|
||||||
|
startSignal = true
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if startSignal {
|
||||||
|
let disposable = generator(currentValue, next).start(next: { next in
|
||||||
|
self.updateValue(next.0)
|
||||||
|
switch next.1 {
|
||||||
|
case let .Some(value):
|
||||||
|
self.subscriber.putNext(value)
|
||||||
|
case .None:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
self.subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
self.headCompleted()
|
||||||
|
})
|
||||||
|
self.currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateValue(_ value: T) {
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
self.value = value
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func headCompleted() {
|
||||||
|
while true {
|
||||||
|
let leftFunction = Atomic(value: false)
|
||||||
|
|
||||||
|
var nextSignal: Signal<(T, Passthrough<T>), E>! = nil
|
||||||
|
|
||||||
|
var terminated = false
|
||||||
|
var currentValue: T!
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
self.executingSignal = false
|
||||||
|
if self.queuedValues.count != 0 {
|
||||||
|
nextSignal = self.generator(self.value, self.queuedValues[0])
|
||||||
|
self.queuedValues.remove(at: 0)
|
||||||
|
self.executingSignal = true
|
||||||
|
} else {
|
||||||
|
currentValue = self.value
|
||||||
|
terminated = self.terminated
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if terminated {
|
||||||
|
self.subscriber.putNext(currentValue)
|
||||||
|
self.subscriber.putCompletion()
|
||||||
|
} else if nextSignal != nil {
|
||||||
|
let disposable = nextSignal.start(next: { next in
|
||||||
|
self.updateValue(next.0)
|
||||||
|
switch next.1 {
|
||||||
|
case let .Some(value):
|
||||||
|
self.subscriber.putNext(value)
|
||||||
|
case .None:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
self.subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
if leftFunction.swap(true) == true {
|
||||||
|
self.headCompleted()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftFunction.swap(true) == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginCompletion() {
|
||||||
|
var executingSignal = false
|
||||||
|
let currentValue: T
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
executingSignal = self.executingSignal
|
||||||
|
self.terminated = true
|
||||||
|
currentValue = self.value
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if !executingSignal {
|
||||||
|
self.subscriber.putNext(currentValue)
|
||||||
|
self.subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
self.currentDisposable.dispose()
|
||||||
|
self.disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reduceLeft<T, E>(_ value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough<T>), E>) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = ReduceQueueState(subscriber: subscriber, value: value, generator: generator)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
submodules/SSignalKit/SwiftSignalKit/Signal_SideEffects.swift
Normal file
116
submodules/SSignalKit/SwiftSignalKit/Signal_SideEffects.swift
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func beforeNext<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let _ = f(next)
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func afterNext<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
let _ = f(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func beforeStarted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
f()
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func beforeCompleted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
f()
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func afterCompleted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
f()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func afterDisposed<T, E, R>(_ f: @escaping() -> R) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
disposable.add(ActionDisposable {
|
||||||
|
let _ = f()
|
||||||
|
})
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withState<T, E, S>(_ signal: Signal<T, E>, _ 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<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = initialState()
|
||||||
|
let disposable = signal.start(next: { vNext in
|
||||||
|
next(vNext, state)
|
||||||
|
subscriber.putNext(vNext)
|
||||||
|
}, error: { vError in
|
||||||
|
error(vError, state)
|
||||||
|
subscriber.putError(vError)
|
||||||
|
}, completed: {
|
||||||
|
completed(state)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
|
disposed(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
submodules/SSignalKit/SwiftSignalKit/Signal_Single.swift
Normal file
32
submodules/SSignalKit/SwiftSignalKit/Signal_Single.swift
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func single<T, E>(_ value: T, _ errorType: E.Type) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func fail<T, E>(_ valueType: T.Type, _ error: E) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putError(error)
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func complete<T, E>(_ valueType: T.Type, _ error: E.Type) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func never<T, E>(_ valueType: T.Type, _ error: E.Type) -> Signal<T, E> {
|
||||||
|
return Signal { _ in
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
73
submodules/SSignalKit/SwiftSignalKit/Signal_Take.swift
Normal file
73
submodules/SSignalKit/SwiftSignalKit/Signal_Take.swift
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func take<T, E>(_ count: Int) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
let counter = Atomic(value: 0)
|
||||||
|
return signal.start(next: { next in
|
||||||
|
var passthrough = false
|
||||||
|
var complete = false
|
||||||
|
let _ = counter.modify { value in
|
||||||
|
let updatedCount = value + 1
|
||||||
|
passthrough = updatedCount <= count
|
||||||
|
complete = updatedCount == count
|
||||||
|
return updatedCount
|
||||||
|
}
|
||||||
|
if passthrough {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
if complete {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SignalTakeAction {
|
||||||
|
public let passthrough: Bool
|
||||||
|
public let complete: Bool
|
||||||
|
|
||||||
|
public init(passthrough: Bool, complete: Bool) {
|
||||||
|
self.passthrough = passthrough
|
||||||
|
self.complete = complete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func take<T, E>(until: @escaping (T) -> SignalTakeAction) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let action = until(next)
|
||||||
|
if action.passthrough {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
if action.complete {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func last<T, E>(signal: Signal<T, E>) -> Signal<T?, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let value = Atomic<T?>(value: nil)
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let _ = value.swap(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(value.with({ $0 }))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
122
submodules/SSignalKit/SwiftSignalKit/Signal_Timing.swift
Normal file
122
submodules/SSignalKit/SwiftSignalKit/Signal_Timing.swift
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func delay<T, E>(_ timeout: Double, queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
queue.async {
|
||||||
|
let timer = Timer(timeout: timeout, repeat: false, completion: {
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, queue: queue)
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
queue.async {
|
||||||
|
timer.invalidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func suspendAwareDelay<T, E>(_ timeout: Double, granularity: Double = 4.0, queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
queue.async {
|
||||||
|
let beginTimestamp = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
let startFinalTimer: () -> Void = {
|
||||||
|
let finalTimeout = beginTimestamp + timeout - CFAbsoluteTimeGetCurrent()
|
||||||
|
let timer = Timer(timeout: max(0.0, finalTimeout), repeat: false, completion: {
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, queue: queue)
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
queue.async {
|
||||||
|
timer.invalidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout <= granularity * 1.1 {
|
||||||
|
startFinalTimer()
|
||||||
|
} else {
|
||||||
|
var invalidateImpl: (() -> Void)?
|
||||||
|
let timer = Timer(timeout: granularity, repeat: true, completion: {
|
||||||
|
let currentTimestamp = CFAbsoluteTimeGetCurrent()
|
||||||
|
if beginTimestamp + timeout - granularity * 1.1 <= currentTimestamp {
|
||||||
|
invalidateImpl?()
|
||||||
|
startFinalTimer()
|
||||||
|
}
|
||||||
|
}, queue: queue)
|
||||||
|
|
||||||
|
invalidateImpl = {
|
||||||
|
queue.async {
|
||||||
|
timer.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
invalidateImpl?()
|
||||||
|
})
|
||||||
|
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func timeout<T, E>(_ timeout: Double, queue: Queue, alternate: Signal<T, E>) -> (Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
let timer = Timer(timeout: timeout, repeat: false, completion: {
|
||||||
|
disposable.set(alternate.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, queue: queue)
|
||||||
|
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
timer.start()
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(ActionDisposable {
|
||||||
|
timer.invalidate()
|
||||||
|
})
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
|
||||||
|
return disposableSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
submodules/SSignalKit/SwiftSignalKit/Subscriber.swift
Normal file
145
submodules/SSignalKit/SwiftSignalKit/Subscriber.swift
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Subscriber<T, E> {
|
||||||
|
private var next: ((T) -> Void)!
|
||||||
|
private var error: ((E) -> Void)!
|
||||||
|
private var completed: (() -> Void)!
|
||||||
|
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
private var terminated = false
|
||||||
|
internal var disposable: Disposable!
|
||||||
|
|
||||||
|
public init(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) {
|
||||||
|
self.next = next
|
||||||
|
self.error = error
|
||||||
|
self.completed = completed
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
var freeDisposable: Disposable?
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if let disposable = self.disposable {
|
||||||
|
freeDisposable = disposable
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
if let freeDisposableValue = freeDisposable {
|
||||||
|
withExtendedLifetime(freeDisposableValue, {
|
||||||
|
})
|
||||||
|
freeDisposable = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func assignDisposable(_ disposable: Disposable) {
|
||||||
|
var dispose = false
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if self.terminated {
|
||||||
|
dispose = true
|
||||||
|
} else {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if dispose {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func markTerminatedWithoutDisposal() {
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
self.terminated = true
|
||||||
|
self.next = nil
|
||||||
|
self.error = nil
|
||||||
|
self.completed = nil
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putNext(_ next: T) {
|
||||||
|
var action: ((T) -> Void)! = nil
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.next
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putError(_ error: E) {
|
||||||
|
var action: ((E) -> Void)! = nil
|
||||||
|
|
||||||
|
var disposeDisposable: Disposable?
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.error
|
||||||
|
self.next = nil
|
||||||
|
self.error = nil
|
||||||
|
self.completed = nil;
|
||||||
|
self.terminated = true
|
||||||
|
disposeDisposable = self.disposable
|
||||||
|
self.disposable = nil
|
||||||
|
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let disposeDisposable = disposeDisposable {
|
||||||
|
disposeDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putCompletion() {
|
||||||
|
var action: (() -> Void)! = nil
|
||||||
|
|
||||||
|
var disposeDisposable: Disposable? = nil
|
||||||
|
|
||||||
|
var next: ((T) -> Void)?
|
||||||
|
var error: ((E) -> Void)?
|
||||||
|
var completed: (() -> Void)?
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.completed
|
||||||
|
next = self.next
|
||||||
|
self.next = nil
|
||||||
|
error = self.error
|
||||||
|
self.error = nil
|
||||||
|
completed = self.completed
|
||||||
|
self.completed = nil
|
||||||
|
self.terminated = true
|
||||||
|
|
||||||
|
disposeDisposable = self.disposable
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
|
||||||
|
if let next = next {
|
||||||
|
withExtendedLifetime(next, {})
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
withExtendedLifetime(error, {})
|
||||||
|
}
|
||||||
|
if let completed = completed {
|
||||||
|
withExtendedLifetime(completed, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let disposeDisposable = disposeDisposable {
|
||||||
|
disposeDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.h
Normal file
23
submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// SwiftSignalKit.h
|
||||||
|
// SwiftSignalKit
|
||||||
|
//
|
||||||
|
// Created by Peter on 10/06/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 SwiftSignalKit.
|
||||||
|
FOUNDATION_EXPORT double SwiftSignalKitVersionNumber;
|
||||||
|
|
||||||
|
//! Project version string for SwiftSignalKit.
|
||||||
|
FOUNDATION_EXPORT const unsigned char SwiftSignalKitVersionString[];
|
||||||
|
|
||||||
|
// In this header, you should import all the public headers of your framework using statements like #import <SwiftSignalKit/PublicHeader.h>
|
||||||
|
|
||||||
|
|
||||||
168
submodules/SSignalKit/SwiftSignalKit/ThreadPool.swift
Normal file
168
submodules/SSignalKit/SwiftSignalKit/ThreadPool.swift
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class ThreadPoolTaskState {
|
||||||
|
public let cancelled = Atomic<Bool>(value: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ThreadPoolTask {
|
||||||
|
private let state = ThreadPoolTaskState()
|
||||||
|
private let action: (ThreadPoolTaskState) -> ()
|
||||||
|
|
||||||
|
public init(_ action: @escaping(ThreadPoolTaskState) -> ()) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute() {
|
||||||
|
if !state.cancelled.with { $0 } {
|
||||||
|
self.action(self.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cancel() {
|
||||||
|
let _ = self.state.cancelled.swap(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ThreadPoolQueue : Equatable {
|
||||||
|
private weak var threadPool: ThreadPool?
|
||||||
|
private var tasks: [ThreadPoolTask] = []
|
||||||
|
|
||||||
|
public init(threadPool: ThreadPool) {
|
||||||
|
self.threadPool = threadPool
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addTask(_ task: ThreadPoolTask) {
|
||||||
|
if let threadPool = self.threadPool {
|
||||||
|
threadPool.workOnQueue(self, action: {
|
||||||
|
self.tasks.append(task)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func popFirstTask() -> ThreadPoolTask? {
|
||||||
|
if self.tasks.count != 0 {
|
||||||
|
let task = self.tasks[0];
|
||||||
|
self.tasks.remove(at: 0)
|
||||||
|
return task
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func hasTasks() -> Bool {
|
||||||
|
return self.tasks.count != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ==(lhs: ThreadPoolQueue, rhs: ThreadPoolQueue) -> Bool {
|
||||||
|
return lhs === rhs
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public final class ThreadPool: NSObject {
|
||||||
|
private var threads: [Thread] = []
|
||||||
|
private var queues: [ThreadPoolQueue] = []
|
||||||
|
private var takenQueues: [ThreadPoolQueue] = []
|
||||||
|
private var mutex: pthread_mutex_t
|
||||||
|
private var condition: pthread_cond_t
|
||||||
|
|
||||||
|
@objc class func threadEntryPoint(_ threadPool: ThreadPool) {
|
||||||
|
var queue: ThreadPoolQueue!
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var task: ThreadPoolTask!
|
||||||
|
|
||||||
|
pthread_mutex_lock(&threadPool.mutex);
|
||||||
|
|
||||||
|
if queue != nil {
|
||||||
|
if let index = threadPool.takenQueues.index(of: queue) {
|
||||||
|
threadPool.takenQueues.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.hasTasks() {
|
||||||
|
threadPool.queues.append(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while threadPool.queues.count == 0 {
|
||||||
|
pthread_cond_wait(&threadPool.condition, &threadPool.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if threadPool.queues.count != 0 {
|
||||||
|
queue = threadPool.queues[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue != nil {
|
||||||
|
task = queue.popFirstTask()
|
||||||
|
threadPool.takenQueues.append(queue)
|
||||||
|
|
||||||
|
if let index = threadPool.queues.index(of: queue) {
|
||||||
|
threadPool.queues.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&threadPool.mutex);
|
||||||
|
|
||||||
|
if task != nil {
|
||||||
|
autoreleasepool {
|
||||||
|
task.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(threadCount: Int, threadPriority: Double) {
|
||||||
|
assert(threadCount > 0, "threadCount < 0")
|
||||||
|
|
||||||
|
self.mutex = pthread_mutex_t()
|
||||||
|
self.condition = pthread_cond_t()
|
||||||
|
pthread_mutex_init(&self.mutex, nil)
|
||||||
|
pthread_cond_init(&self.condition, nil)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
for _ in 0 ..< threadCount {
|
||||||
|
let thread = Thread(target: ThreadPool.self, selector: #selector(ThreadPool.threadEntryPoint(_:)), object: self)
|
||||||
|
thread.threadPriority = threadPriority
|
||||||
|
self.threads.append(thread)
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.mutex)
|
||||||
|
pthread_cond_destroy(&self.condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addTask(_ task: ThreadPoolTask) {
|
||||||
|
let tempQueue = self.nextQueue()
|
||||||
|
tempQueue.addTask(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func workOnQueue(_ queue: ThreadPoolQueue, action: () -> ()) {
|
||||||
|
pthread_mutex_lock(&self.mutex)
|
||||||
|
action()
|
||||||
|
if !self.queues.contains(queue) && !self.takenQueues.contains(queue) {
|
||||||
|
self.queues.append(queue)
|
||||||
|
}
|
||||||
|
pthread_cond_broadcast(&self.condition)
|
||||||
|
pthread_mutex_unlock(&self.mutex)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nextQueue() -> ThreadPoolQueue {
|
||||||
|
return ThreadPoolQueue(threadPool: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isCurrentThreadInPool() -> Bool {
|
||||||
|
let currentThread = Thread.current
|
||||||
|
for thread in self.threads {
|
||||||
|
if currentThread.isEqual(thread) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
52
submodules/SSignalKit/SwiftSignalKit/Timer.swift
Normal file
52
submodules/SSignalKit/SwiftSignalKit/Timer.swift
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Timer {
|
||||||
|
private let timer = Atomic<DispatchSourceTimer?>(value: nil)
|
||||||
|
private let timeout: Double
|
||||||
|
private let `repeat`: Bool
|
||||||
|
private let completion: () -> Void
|
||||||
|
private let queue: Queue
|
||||||
|
|
||||||
|
public init(timeout: Double, `repeat`: Bool, completion: @escaping() -> Void, queue: Queue) {
|
||||||
|
self.timeout = timeout
|
||||||
|
self.`repeat` = `repeat`
|
||||||
|
self.completion = completion
|
||||||
|
self.queue = queue
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func start() {
|
||||||
|
let timer = DispatchSource.makeTimerSource(queue: self.queue.queue)
|
||||||
|
timer.setEventHandler(handler: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.completion()
|
||||||
|
if !strongSelf.`repeat` {
|
||||||
|
strongSelf.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let _ = self.timer.modify { _ in
|
||||||
|
return timer
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.`repeat` {
|
||||||
|
let time: DispatchTime = DispatchTime.now() + self.timeout
|
||||||
|
timer.scheduleRepeating(deadline: time, interval: self.timeout)
|
||||||
|
} else {
|
||||||
|
let time: DispatchTime = DispatchTime.now() + self.timeout
|
||||||
|
timer.scheduleOneshot(deadline: time)
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func invalidate() {
|
||||||
|
let _ = self.timer.modify { timer in
|
||||||
|
timer?.cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
submodules/SSignalKit/SwiftSignalKit/ValuePipe.swift
Normal file
39
submodules/SSignalKit/SwiftSignalKit/ValuePipe.swift
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class ValuePipe<T> {
|
||||||
|
private let subscribers = Atomic(value: Bag<(T) -> Void>())
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func signal() -> Signal<T, NoError> {
|
||||||
|
return Signal { [weak self] subscriber in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let index = strongSelf.subscribers.with { value -> Bag<T>.Index in
|
||||||
|
return value.add { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable { [weak strongSelf] in
|
||||||
|
if let strongSelf = strongSelf {
|
||||||
|
strongSelf.subscribers.with { value -> Void in
|
||||||
|
value.remove(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putNext(_ next: T) {
|
||||||
|
let items = self.subscribers.with { value -> [(T) -> Void] in
|
||||||
|
return value.copyItems()
|
||||||
|
}
|
||||||
|
for f in items {
|
||||||
|
f(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
submodules/SSignalKit/SwiftSignalKitMac/Info.plist
Normal file
26
submodules/SSignalKit/SwiftSignalKitMac/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2016 Telegram. All rights reserved.</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
19
submodules/SSignalKit/SwiftSignalKitMac/SwiftSignalKitMac.h
Normal file
19
submodules/SSignalKit/SwiftSignalKitMac/SwiftSignalKitMac.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// SwiftSignalKitMac.h
|
||||||
|
// SwiftSignalKitMac
|
||||||
|
//
|
||||||
|
// Created by Peter on 9/5/16.
|
||||||
|
// Copyright © 2016 Telegram. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
//! Project version number for SwiftSignalKitMac.
|
||||||
|
FOUNDATION_EXPORT double SwiftSignalKitMacVersionNumber;
|
||||||
|
|
||||||
|
//! Project version string for SwiftSignalKitMac.
|
||||||
|
FOUNDATION_EXPORT const unsigned char SwiftSignalKitMacVersionString[];
|
||||||
|
|
||||||
|
// In this header, you should import all the public headers of your framework using statements like #import <SwiftSignalKitMac/PublicHeader.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
internal class DeallocatingObject : CustomStringConvertible {
|
||||||
|
private var deallocated: UnsafeMutablePointer<Bool>
|
||||||
|
|
||||||
|
init(deallocated: UnsafeMutablePointer<Bool>) {
|
||||||
|
self.deallocated = deallocated
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.deallocated.pointee = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
get {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
submodules/SSignalKit/SwiftSignalKitTests/Info.plist
Normal file
24
submodules/SSignalKit/SwiftSignalKitTests/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
166
submodules/SSignalKit/SwiftSignalKitTests/PerformanceTests.swift
Normal file
166
submodules/SSignalKit/SwiftSignalKitTests/PerformanceTests.swift
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
final class DisposableLock {
|
||||||
|
private var action: (() -> Void)?
|
||||||
|
private var lock = pthread_mutex_t()
|
||||||
|
|
||||||
|
init(action: @escaping () -> Void) {
|
||||||
|
self.action = action
|
||||||
|
pthread_mutex_init(&self.lock, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
var action: (() -> Void)?
|
||||||
|
pthread_mutex_lock(&self.lock)
|
||||||
|
action = self.action
|
||||||
|
self.action = nil
|
||||||
|
pthread_mutex_unlock(&self.lock)
|
||||||
|
if let action = action {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DisposableSpinLock {
|
||||||
|
private var action: (() -> Void)?
|
||||||
|
private var lock = OSSpinLock()
|
||||||
|
|
||||||
|
init(action: @escaping () -> Void) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
var action: (() -> Void)?
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
action = self.action
|
||||||
|
self.action = nil
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
if let action = action {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DisposableNoLock {
|
||||||
|
private var action: (() -> Void)?
|
||||||
|
|
||||||
|
init(action: @escaping () -> Void) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
var action: (() -> Void)?
|
||||||
|
action = self.action
|
||||||
|
self.action = nil
|
||||||
|
if let action = action {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DisposableAtomic {
|
||||||
|
private var action: () -> Void
|
||||||
|
private var disposed: Int32 = 0
|
||||||
|
|
||||||
|
init(action: @escaping () -> Void) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
if OSAtomicCompareAndSwap32(0, 1, &self.disposed) {
|
||||||
|
self.action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PerformanceTests: XCTestCase {
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeasureLock() {
|
||||||
|
measure {
|
||||||
|
for _ in 0 ..< 1000000 {
|
||||||
|
let disposable = DisposableLock(action: {})
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeasureSpinlock() {
|
||||||
|
measure {
|
||||||
|
for _ in 0 ..< 1000000 {
|
||||||
|
let disposable = DisposableSpinLock(action: {})
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMeasureAtomic() {
|
||||||
|
measure {
|
||||||
|
for _ in 0 ..< 1000000 {
|
||||||
|
let disposable = DisposableAtomic(action: {})
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func read(_ idxin: Int, _ size: Int, _ tree: inout [Int: Int], _ reads: inout Set<Int>) -> Int {
|
||||||
|
var idx = idxin
|
||||||
|
var sum = 0
|
||||||
|
while idx <= size {
|
||||||
|
print("read at \(idx)")
|
||||||
|
if let value = tree[idx] {
|
||||||
|
sum += value
|
||||||
|
}
|
||||||
|
reads.insert(idx)
|
||||||
|
idx += (idx & -idx)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(_ idxin: Int, _ val: Int, _ tree: inout [Int: Int], _ updates: inout Set<Int>) {
|
||||||
|
var idx = idxin
|
||||||
|
while (idx > 0) {
|
||||||
|
if let value = tree[idx] {
|
||||||
|
tree[idx] = value + val
|
||||||
|
} else {
|
||||||
|
tree[idx] = val
|
||||||
|
}
|
||||||
|
//print("write at \(idx)")
|
||||||
|
updates.insert(idx)
|
||||||
|
idx -= (idx & -idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTree() {
|
||||||
|
let size = 2_000_000
|
||||||
|
var dict: [Int: Int] = [:]
|
||||||
|
|
||||||
|
var updates = Set<Int>()
|
||||||
|
var index = 0
|
||||||
|
for _ in 1 ..< 100_000 {
|
||||||
|
//update(Int(1 + arc4random_uniform(UInt32(size))), 1, &dict, &updates)
|
||||||
|
update(index, 1, &dict, &updates)
|
||||||
|
index += Int(1 + arc4random_uniform(100))
|
||||||
|
}
|
||||||
|
update(size - 1, 1, &dict, &updates)
|
||||||
|
print("update ops = \(updates.count), tree = \(dict.count) items")
|
||||||
|
|
||||||
|
var reads = Set<Int>()
|
||||||
|
let sum = read(1, size, &dict, &reads)
|
||||||
|
print("read = \(sum) ops = \(reads.count)")
|
||||||
|
|
||||||
|
update(99, -2, &dict, &updates)
|
||||||
|
reads.removeAll()
|
||||||
|
let sum2 = read(1, size, &dict, &reads)
|
||||||
|
print("read2 = \(sum2) ops = \(reads.count)")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
class SwiftSignalKitTests: XCTestCase {
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testActionDisposableDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
object = nil
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testActionDisposableNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
let object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let _ = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
let object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(disposable)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableDisposedMultipleTimes() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
let object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
let _ = object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
let _ = object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(actionDisposable1)
|
||||||
|
metaDisposable.set(actionDisposable2)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
let object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetSingleDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
let object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
disposableSet.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetMultipleDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
let object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
let _ = object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
let _ = object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
disposableSet.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetSingleNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
let object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
let _ = object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetMultipleNotDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
let object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
let _ = object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
let _ = object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertFalse(disposed1, "disposed1 != false")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertFalse(disposed2, "disposed2 != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableAlreadyDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
let object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
let _ = object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
let _ = object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(actionDisposable1)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
metaDisposable.set(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetAlreadyDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
let object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
let _ = object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
let _ = object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.dispose()
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDelayed1() {
|
||||||
|
var flag = false
|
||||||
|
let signal = Signal<Signal<Void, NoError>, NoError> { subscriber in
|
||||||
|
Queue.concurrentDefaultQueue().after(0.1, {
|
||||||
|
subscriber.putNext(Signal { susbcriber2 in
|
||||||
|
return ActionDisposable {
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
} |> switchToLatest
|
||||||
|
|
||||||
|
let disposable = signal.start()
|
||||||
|
disposable.dispose()
|
||||||
|
|
||||||
|
usleep(1000000 * 20)
|
||||||
|
|
||||||
|
XCTAssert(flag == true)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user