You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pool.c 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under both the BSD-style license (found in the
  6. * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  7. * in the COPYING file in the root directory of this source tree).
  8. * You may select, at your option, one of the above-listed licenses.
  9. */
  10. /* ====== Dependencies ======= */
  11. #include <stddef.h> /* size_t */
  12. #include <stdlib.h> /* malloc, calloc, free */
  13. #include "pool.h"
  14. /* ====== Compiler specifics ====== */
  15. #if defined(_MSC_VER)
  16. # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
  17. #endif
  18. #ifdef ZSTD_MULTITHREAD
  19. #include "threading.h" /* pthread adaptation */
  20. /* A job is a function and an opaque argument */
  21. typedef struct POOL_job_s {
  22. POOL_function function;
  23. void *opaque;
  24. } POOL_job;
  25. struct POOL_ctx_s {
  26. ZSTD_customMem customMem;
  27. /* Keep track of the threads */
  28. pthread_t *threads;
  29. size_t numThreads;
  30. /* The queue is a circular buffer */
  31. POOL_job *queue;
  32. size_t queueHead;
  33. size_t queueTail;
  34. size_t queueSize;
  35. /* The number of threads working on jobs */
  36. size_t numThreadsBusy;
  37. /* Indicates if the queue is empty */
  38. int queueEmpty;
  39. /* The mutex protects the queue */
  40. pthread_mutex_t queueMutex;
  41. /* Condition variable for pushers to wait on when the queue is full */
  42. pthread_cond_t queuePushCond;
  43. /* Condition variables for poppers to wait on when the queue is empty */
  44. pthread_cond_t queuePopCond;
  45. /* Indicates if the queue is shutting down */
  46. int shutdown;
  47. };
  48. /* POOL_thread() :
  49. Work thread for the thread pool.
  50. Waits for jobs and executes them.
  51. @returns : NULL on failure else non-null.
  52. */
  53. static void* POOL_thread(void* opaque) {
  54. POOL_ctx* const ctx = (POOL_ctx*)opaque;
  55. if (!ctx) { return NULL; }
  56. for (;;) {
  57. /* Lock the mutex and wait for a non-empty queue or until shutdown */
  58. pthread_mutex_lock(&ctx->queueMutex);
  59. while (ctx->queueEmpty && !ctx->shutdown) {
  60. pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex);
  61. }
  62. /* empty => shutting down: so stop */
  63. if (ctx->queueEmpty) {
  64. pthread_mutex_unlock(&ctx->queueMutex);
  65. return opaque;
  66. }
  67. /* Pop a job off the queue */
  68. { POOL_job const job = ctx->queue[ctx->queueHead];
  69. ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;
  70. ctx->numThreadsBusy++;
  71. ctx->queueEmpty = ctx->queueHead == ctx->queueTail;
  72. /* Unlock the mutex, signal a pusher, and run the job */
  73. pthread_mutex_unlock(&ctx->queueMutex);
  74. pthread_cond_signal(&ctx->queuePushCond);
  75. job.function(job.opaque);
  76. /* If the intended queue size was 0, signal after finishing job */
  77. if (ctx->queueSize == 1) {
  78. pthread_mutex_lock(&ctx->queueMutex);
  79. ctx->numThreadsBusy--;
  80. pthread_mutex_unlock(&ctx->queueMutex);
  81. pthread_cond_signal(&ctx->queuePushCond);
  82. } }
  83. } /* for (;;) */
  84. /* Unreachable */
  85. }
  86. POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
  87. return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
  88. }
  89. POOL_ctx *POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) {
  90. POOL_ctx *ctx;
  91. /* Check the parameters */
  92. if (!numThreads) { return NULL; }
  93. /* Allocate the context and zero initialize */
  94. ctx = (POOL_ctx *)ZSTD_calloc(sizeof(POOL_ctx), customMem);
  95. if (!ctx) { return NULL; }
  96. /* Initialize the job queue.
  97. * It needs one extra space since one space is wasted to differentiate empty
  98. * and full queues.
  99. */
  100. ctx->queueSize = queueSize + 1;
  101. ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job));
  102. ctx->queueHead = 0;
  103. ctx->queueTail = 0;
  104. ctx->numThreadsBusy = 0;
  105. ctx->queueEmpty = 1;
  106. (void)pthread_mutex_init(&ctx->queueMutex, NULL);
  107. (void)pthread_cond_init(&ctx->queuePushCond, NULL);
  108. (void)pthread_cond_init(&ctx->queuePopCond, NULL);
  109. ctx->shutdown = 0;
  110. /* Allocate space for the thread handles */
  111. ctx->threads = (pthread_t*)ZSTD_malloc(numThreads * sizeof(pthread_t), customMem);
  112. ctx->numThreads = 0;
  113. ctx->customMem = customMem;
  114. /* Check for errors */
  115. if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; }
  116. /* Initialize the threads */
  117. { size_t i;
  118. for (i = 0; i < numThreads; ++i) {
  119. if (pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) {
  120. ctx->numThreads = i;
  121. POOL_free(ctx);
  122. return NULL;
  123. } }
  124. ctx->numThreads = numThreads;
  125. }
  126. return ctx;
  127. }
  128. /*! POOL_join() :
  129. Shutdown the queue, wake any sleeping threads, and join all of the threads.
  130. */
  131. static void POOL_join(POOL_ctx *ctx) {
  132. /* Shut down the queue */
  133. pthread_mutex_lock(&ctx->queueMutex);
  134. ctx->shutdown = 1;
  135. pthread_mutex_unlock(&ctx->queueMutex);
  136. /* Wake up sleeping threads */
  137. pthread_cond_broadcast(&ctx->queuePushCond);
  138. pthread_cond_broadcast(&ctx->queuePopCond);
  139. /* Join all of the threads */
  140. { size_t i;
  141. for (i = 0; i < ctx->numThreads; ++i) {
  142. pthread_join(ctx->threads[i], NULL);
  143. } }
  144. }
  145. void POOL_free(POOL_ctx *ctx) {
  146. if (!ctx) { return; }
  147. POOL_join(ctx);
  148. pthread_mutex_destroy(&ctx->queueMutex);
  149. pthread_cond_destroy(&ctx->queuePushCond);
  150. pthread_cond_destroy(&ctx->queuePopCond);
  151. ZSTD_free(ctx->queue, ctx->customMem);
  152. ZSTD_free(ctx->threads, ctx->customMem);
  153. ZSTD_free(ctx, ctx->customMem);
  154. }
  155. size_t POOL_sizeof(POOL_ctx *ctx) {
  156. if (ctx==NULL) return 0; /* supports sizeof NULL */
  157. return sizeof(*ctx)
  158. + ctx->queueSize * sizeof(POOL_job)
  159. + ctx->numThreads * sizeof(pthread_t);
  160. }
  161. /**
  162. * Returns 1 if the queue is full and 0 otherwise.
  163. *
  164. * If the queueSize is 1 (the pool was created with an intended queueSize of 0),
  165. * then a queue is empty if there is a thread free and no job is waiting.
  166. */
  167. static int isQueueFull(POOL_ctx const* ctx) {
  168. if (ctx->queueSize > 1) {
  169. return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize);
  170. } else {
  171. return ctx->numThreadsBusy == ctx->numThreads ||
  172. !ctx->queueEmpty;
  173. }
  174. }
  175. void POOL_add(void* ctxVoid, POOL_function function, void *opaque) {
  176. POOL_ctx* const ctx = (POOL_ctx*)ctxVoid;
  177. if (!ctx) { return; }
  178. pthread_mutex_lock(&ctx->queueMutex);
  179. { POOL_job const job = {function, opaque};
  180. /* Wait until there is space in the queue for the new job */
  181. while (isQueueFull(ctx) && !ctx->shutdown) {
  182. pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
  183. }
  184. /* The queue is still going => there is space */
  185. if (!ctx->shutdown) {
  186. ctx->queueEmpty = 0;
  187. ctx->queue[ctx->queueTail] = job;
  188. ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
  189. }
  190. }
  191. pthread_mutex_unlock(&ctx->queueMutex);
  192. pthread_cond_signal(&ctx->queuePopCond);
  193. }
  194. #else /* ZSTD_MULTITHREAD not defined */
  195. /* No multi-threading support */
  196. /* We don't need any data, but if it is empty malloc() might return NULL. */
  197. struct POOL_ctx_s {
  198. int dummy;
  199. };
  200. static POOL_ctx g_ctx;
  201. POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
  202. return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
  203. }
  204. POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) {
  205. (void)numThreads;
  206. (void)queueSize;
  207. (void)customMem;
  208. return &g_ctx;
  209. }
  210. void POOL_free(POOL_ctx* ctx) {
  211. assert(!ctx || ctx == &g_ctx);
  212. (void)ctx;
  213. }
  214. void POOL_add(void* ctx, POOL_function function, void* opaque) {
  215. (void)ctx;
  216. function(opaque);
  217. }
  218. size_t POOL_sizeof(POOL_ctx* ctx) {
  219. if (ctx==NULL) return 0; /* supports sizeof NULL */
  220. assert(ctx == &g_ctx);
  221. return sizeof(*ctx);
  222. }
  223. #endif /* ZSTD_MULTITHREAD */