/*
|
* Copyright (c) 2014, Facebook, Inc.
|
* All rights reserved.
|
*
|
* This source code is licensed under the BSD-style license found in the
|
* LICENSE file in the root directory of this source tree. An additional grant
|
* of patent rights can be found in the PATENTS file in the same directory.
|
*
|
*/
|
|
#import "OSSExecutor.h"
|
|
#import <pthread.h>
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
/*!
|
Get the remaining stack-size of the current thread.
|
|
@param totalSize The total stack size of the current thread.
|
|
@return The remaining size, in bytes, available to the current thread.
|
|
@note This function cannot be inlined, as otherwise the internal implementation could fail to report the proper
|
remaining stack space.
|
*/
|
__attribute__((noinline)) static size_t remaining_stack_size(size_t *restrict totalSize) {
|
pthread_t currentThread = pthread_self();
|
|
// NOTE: We must store stack pointers as uint8_t so that the pointer math is well-defined
|
uint8_t *endStack = pthread_get_stackaddr_np(currentThread);
|
*totalSize = pthread_get_stacksize_np(currentThread);
|
|
// NOTE: If the function is inlined, this value could be incorrect
|
uint8_t *frameAddr = __builtin_frame_address(0);
|
|
return (*totalSize) - (endStack - frameAddr);
|
}
|
|
@interface OSSExecutor ()
|
|
@property (nonatomic, copy) void(^block)(void(^block)(void));
|
|
@end
|
|
@implementation OSSExecutor
|
|
#pragma mark - Executor methods
|
|
+ (instancetype)defaultExecutor {
|
static OSSExecutor *defaultExecutor = NULL;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
defaultExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
// We prefer to run everything possible immediately, so that there is callstack information
|
// when debugging. However, we don't want the stack to get too deep, so if the remaining stack space
|
// is less than 10% of the total space, we dispatch to another GCD queue.
|
size_t totalStackSize = 0;
|
size_t remainingStackSize = remaining_stack_size(&totalStackSize);
|
|
if (remainingStackSize < (totalStackSize / 10)) {
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
|
} else {
|
@autoreleasepool {
|
block();
|
}
|
}
|
}];
|
});
|
return defaultExecutor;
|
}
|
|
+ (instancetype)immediateExecutor {
|
static OSSExecutor *immediateExecutor = NULL;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
immediateExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
block();
|
}];
|
});
|
return immediateExecutor;
|
}
|
|
+ (instancetype)mainThreadExecutor {
|
static OSSExecutor *mainThreadExecutor = NULL;
|
static dispatch_once_t onceToken;
|
dispatch_once(&onceToken, ^{
|
mainThreadExecutor = [self executorWithBlock:^void(void(^block)(void)) {
|
if (![NSThread isMainThread]) {
|
dispatch_async(dispatch_get_main_queue(), block);
|
} else {
|
@autoreleasepool {
|
block();
|
}
|
}
|
}];
|
});
|
return mainThreadExecutor;
|
}
|
|
+ (instancetype)executorWithBlock:(void(^)(void(^block)(void)))block {
|
return [[self alloc] initWithBlock:block];
|
}
|
|
+ (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue {
|
return [self executorWithBlock:^void(void(^block)(void)) {
|
dispatch_async(queue, block);
|
}];
|
}
|
|
+ (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue {
|
return [self executorWithBlock:^void(void(^block)(void)) {
|
[queue addOperation:[NSBlockOperation blockOperationWithBlock:block]];
|
}];
|
}
|
|
#pragma mark - Initializer
|
|
- (instancetype)initWithBlock:(void(^)(void(^block)(void)))block {
|
self = [super init];
|
if (!self) return self;
|
|
_block = block;
|
|
return self;
|
}
|
|
#pragma mark - Execution
|
|
- (void)execute:(void(^)(void))block {
|
self.block(block);
|
}
|
|
@end
|
|
NS_ASSUME_NONNULL_END
|