YYDispatchQueuePool.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. //
  2. // YYDispatchQueueManager.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/7/18.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYDispatchQueuePool.h"
  12. #import <UIKit/UIKit.h>
  13. #import <libkern/OSAtomic.h>
  14. #define MAX_QUEUE_COUNT 32
  15. static inline dispatch_queue_priority_t NSQualityOfServiceToDispatchPriority(NSQualityOfService qos) {
  16. switch (qos) {
  17. case NSQualityOfServiceUserInteractive: return DISPATCH_QUEUE_PRIORITY_HIGH;
  18. case NSQualityOfServiceUserInitiated: return DISPATCH_QUEUE_PRIORITY_HIGH;
  19. case NSQualityOfServiceUtility: return DISPATCH_QUEUE_PRIORITY_LOW;
  20. case NSQualityOfServiceBackground: return DISPATCH_QUEUE_PRIORITY_BACKGROUND;
  21. case NSQualityOfServiceDefault: return DISPATCH_QUEUE_PRIORITY_DEFAULT;
  22. default: return DISPATCH_QUEUE_PRIORITY_DEFAULT;
  23. }
  24. }
  25. static inline qos_class_t NSQualityOfServiceToQOSClass(NSQualityOfService qos) {
  26. switch (qos) {
  27. case NSQualityOfServiceUserInteractive: return QOS_CLASS_USER_INTERACTIVE;
  28. case NSQualityOfServiceUserInitiated: return QOS_CLASS_USER_INITIATED;
  29. case NSQualityOfServiceUtility: return QOS_CLASS_UTILITY;
  30. case NSQualityOfServiceBackground: return QOS_CLASS_BACKGROUND;
  31. case NSQualityOfServiceDefault: return QOS_CLASS_DEFAULT;
  32. default: return QOS_CLASS_UNSPECIFIED;
  33. }
  34. }
  35. typedef struct {
  36. const char *name;
  37. void **queues;
  38. uint32_t queueCount;
  39. int32_t counter;
  40. } YYDispatchContext;
  41. static YYDispatchContext *YYDispatchContextCreate(const char *name,
  42. uint32_t queueCount,
  43. NSQualityOfService qos) {
  44. YYDispatchContext *context = calloc(1, sizeof(YYDispatchContext));
  45. if (!context) return NULL;
  46. context->queues = calloc(queueCount, sizeof(void *));
  47. if (!context->queues) {
  48. free(context);
  49. return NULL;
  50. }
  51. if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
  52. dispatch_qos_class_t qosClass = NSQualityOfServiceToQOSClass(qos);
  53. for (NSUInteger i = 0; i < queueCount; i++) {
  54. dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0);
  55. dispatch_queue_t queue = dispatch_queue_create(name, attr);
  56. context->queues[i] = (__bridge_retained void *)(queue);
  57. }
  58. } else {
  59. long identifier = NSQualityOfServiceToDispatchPriority(qos);
  60. for (NSUInteger i = 0; i < queueCount; i++) {
  61. dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
  62. dispatch_set_target_queue(queue, dispatch_get_global_queue(identifier, 0));
  63. context->queues[i] = (__bridge_retained void *)(queue);
  64. }
  65. }
  66. context->queueCount = queueCount;
  67. if (name) {
  68. context->name = strdup(name);
  69. }
  70. return context;
  71. }
  72. static void YYDispatchContextRelease(YYDispatchContext *context) {
  73. if (!context) return;
  74. if (context->queues) {
  75. for (NSUInteger i = 0; i < context->queueCount; i++) {
  76. void *queuePointer = context->queues[i];
  77. dispatch_queue_t queue = (__bridge_transfer dispatch_queue_t)(queuePointer);
  78. const char *name = dispatch_queue_get_label(queue);
  79. if (name) strlen(name); // avoid compiler warning
  80. queue = nil;
  81. }
  82. free(context->queues);
  83. context->queues = NULL;
  84. }
  85. if (context->name) free((void *)context->name);
  86. }
  87. static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {
  88. uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);
  89. void *queue = context->queues[counter % context->queueCount];
  90. return (__bridge dispatch_queue_t)(queue);
  91. }
  92. static YYDispatchContext *YYDispatchContextGetForQOS(NSQualityOfService qos) {
  93. static YYDispatchContext *context[5] = {0};
  94. switch (qos) {
  95. case NSQualityOfServiceUserInteractive: {
  96. static dispatch_once_t onceToken;
  97. dispatch_once(&onceToken, ^{
  98. int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
  99. count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
  100. context[0] = YYDispatchContextCreate("com.ibireme.yykit.user-interactive", count, qos);
  101. });
  102. return context[0];
  103. } break;
  104. case NSQualityOfServiceUserInitiated: {
  105. static dispatch_once_t onceToken;
  106. dispatch_once(&onceToken, ^{
  107. int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
  108. count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
  109. context[1] = YYDispatchContextCreate("com.ibireme.yykit.user-initiated", count, qos);
  110. });
  111. return context[1];
  112. } break;
  113. case NSQualityOfServiceUtility: {
  114. static dispatch_once_t onceToken;
  115. dispatch_once(&onceToken, ^{
  116. int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
  117. count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
  118. context[2] = YYDispatchContextCreate("com.ibireme.yykit.utility", count, qos);
  119. });
  120. return context[2];
  121. } break;
  122. case NSQualityOfServiceBackground: {
  123. static dispatch_once_t onceToken;
  124. dispatch_once(&onceToken, ^{
  125. int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
  126. count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
  127. context[3] = YYDispatchContextCreate("com.ibireme.yykit.background", count, qos);
  128. });
  129. return context[3];
  130. } break;
  131. case NSQualityOfServiceDefault:
  132. default: {
  133. static dispatch_once_t onceToken;
  134. dispatch_once(&onceToken, ^{
  135. int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
  136. count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
  137. context[4] = YYDispatchContextCreate("com.ibireme.yykit.default", count, qos);
  138. });
  139. return context[4];
  140. } break;
  141. }
  142. }
  143. @implementation YYDispatchQueuePool {
  144. @public
  145. YYDispatchContext *_context;
  146. }
  147. - (void)dealloc {
  148. if (_context) {
  149. YYDispatchContextRelease(_context);
  150. _context = NULL;
  151. }
  152. }
  153. - (instancetype)initWithContext:(YYDispatchContext *)context {
  154. self = [super init];
  155. if (!context) return nil;
  156. self->_context = context;
  157. _name = context->name ? [NSString stringWithUTF8String:context->name] : nil;
  158. return self;
  159. }
  160. - (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos {
  161. if (queueCount == 0 || queueCount > MAX_QUEUE_COUNT) return nil;
  162. self = [super init];
  163. _context = YYDispatchContextCreate(name.UTF8String, (uint32_t)queueCount, qos);
  164. if (!_context) return nil;
  165. _name = name;
  166. return self;
  167. }
  168. - (dispatch_queue_t)queue {
  169. return YYDispatchContextGetQueue(_context);
  170. }
  171. + (instancetype)defaultPoolForQOS:(NSQualityOfService)qos {
  172. switch (qos) {
  173. case NSQualityOfServiceUserInteractive: {
  174. static YYDispatchQueuePool *pool;
  175. static dispatch_once_t onceToken;
  176. dispatch_once(&onceToken, ^{
  177. pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
  178. });
  179. return pool;
  180. } break;
  181. case NSQualityOfServiceUserInitiated: {
  182. static YYDispatchQueuePool *pool;
  183. static dispatch_once_t onceToken;
  184. dispatch_once(&onceToken, ^{
  185. pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
  186. });
  187. return pool;
  188. } break;
  189. case NSQualityOfServiceUtility: {
  190. static YYDispatchQueuePool *pool;
  191. static dispatch_once_t onceToken;
  192. dispatch_once(&onceToken, ^{
  193. pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
  194. });
  195. return pool;
  196. } break;
  197. case NSQualityOfServiceBackground: {
  198. static YYDispatchQueuePool *pool;
  199. static dispatch_once_t onceToken;
  200. dispatch_once(&onceToken, ^{
  201. pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
  202. });
  203. return pool;
  204. } break;
  205. case NSQualityOfServiceDefault:
  206. default: {
  207. static YYDispatchQueuePool *pool;
  208. static dispatch_once_t onceToken;
  209. dispatch_once(&onceToken, ^{
  210. pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(NSQualityOfServiceDefault)];
  211. });
  212. return pool;
  213. } break;
  214. }
  215. }
  216. @end
  217. dispatch_queue_t YYDispatchQueueGetForQOS(NSQualityOfService qos) {
  218. return YYDispatchContextGetQueue(YYDispatchContextGetForQOS(qos));
  219. }