OSSExecutor.m 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /*
  2. * Copyright (c) 2014, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #import "OSSExecutor.h"
  11. #import <pthread.h>
  12. NS_ASSUME_NONNULL_BEGIN
  13. /*!
  14. Get the remaining stack-size of the current thread.
  15. @param totalSize The total stack size of the current thread.
  16. @return The remaining size, in bytes, available to the current thread.
  17. @note This function cannot be inlined, as otherwise the internal implementation could fail to report the proper
  18. remaining stack space.
  19. */
  20. __attribute__((noinline)) static size_t remaining_stack_size(size_t *restrict totalSize) {
  21. pthread_t currentThread = pthread_self();
  22. // NOTE: We must store stack pointers as uint8_t so that the pointer math is well-defined
  23. uint8_t *endStack = pthread_get_stackaddr_np(currentThread);
  24. *totalSize = pthread_get_stacksize_np(currentThread);
  25. // NOTE: If the function is inlined, this value could be incorrect
  26. uint8_t *frameAddr = __builtin_frame_address(0);
  27. return (*totalSize) - (endStack - frameAddr);
  28. }
  29. @interface OSSExecutor ()
  30. @property (nonatomic, copy) void(^block)(void(^block)(void));
  31. @end
  32. @implementation OSSExecutor
  33. #pragma mark - Executor methods
  34. + (instancetype)defaultExecutor {
  35. static OSSExecutor *defaultExecutor = NULL;
  36. static dispatch_once_t onceToken;
  37. dispatch_once(&onceToken, ^{
  38. defaultExecutor = [self executorWithBlock:^void(void(^block)(void)) {
  39. // We prefer to run everything possible immediately, so that there is callstack information
  40. // when debugging. However, we don't want the stack to get too deep, so if the remaining stack space
  41. // is less than 10% of the total space, we dispatch to another GCD queue.
  42. size_t totalStackSize = 0;
  43. size_t remainingStackSize = remaining_stack_size(&totalStackSize);
  44. if (remainingStackSize < (totalStackSize / 10)) {
  45. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
  46. } else {
  47. @autoreleasepool {
  48. block();
  49. }
  50. }
  51. }];
  52. });
  53. return defaultExecutor;
  54. }
  55. + (instancetype)immediateExecutor {
  56. static OSSExecutor *immediateExecutor = NULL;
  57. static dispatch_once_t onceToken;
  58. dispatch_once(&onceToken, ^{
  59. immediateExecutor = [self executorWithBlock:^void(void(^block)(void)) {
  60. block();
  61. }];
  62. });
  63. return immediateExecutor;
  64. }
  65. + (instancetype)mainThreadExecutor {
  66. static OSSExecutor *mainThreadExecutor = NULL;
  67. static dispatch_once_t onceToken;
  68. dispatch_once(&onceToken, ^{
  69. mainThreadExecutor = [self executorWithBlock:^void(void(^block)(void)) {
  70. if (![NSThread isMainThread]) {
  71. dispatch_async(dispatch_get_main_queue(), block);
  72. } else {
  73. @autoreleasepool {
  74. block();
  75. }
  76. }
  77. }];
  78. });
  79. return mainThreadExecutor;
  80. }
  81. + (instancetype)executorWithBlock:(void(^)(void(^block)(void)))block {
  82. return [[self alloc] initWithBlock:block];
  83. }
  84. + (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue {
  85. return [self executorWithBlock:^void(void(^block)(void)) {
  86. dispatch_async(queue, block);
  87. }];
  88. }
  89. + (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue {
  90. return [self executorWithBlock:^void(void(^block)(void)) {
  91. [queue addOperation:[NSBlockOperation blockOperationWithBlock:block]];
  92. }];
  93. }
  94. #pragma mark - Initializer
  95. - (instancetype)initWithBlock:(void(^)(void(^block)(void)))block {
  96. self = [super init];
  97. if (!self) return self;
  98. _block = block;
  99. return self;
  100. }
  101. #pragma mark - Execution
  102. - (void)execute:(void(^)(void))block {
  103. self.block(block);
  104. }
  105. @end
  106. NS_ASSUME_NONNULL_END