2 * Copyright (c) 2016-present, Facebook, Inc.
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.
11 /* ====== Dependencies ======= */
12 #include <stddef.h> /* size_t */
13 #include <stdlib.h> /* malloc, calloc, free */
16 /* ====== Compiler specifics ====== */
18 # pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
22 #ifdef ZSTD_MULTITHREAD
24 #include "threading.h" /* pthread adaptation */
26 /* A job is a function and an opaque argument */
27 typedef struct POOL_job_s {
28 POOL_function function;
33 /* Keep track of the threads */
37 /* The queue is a circular buffer */
42 /* The mutex protects the queue */
43 pthread_mutex_t queueMutex;
44 /* Condition variable for pushers to wait on when the queue is full */
45 pthread_cond_t queuePushCond;
46 /* Condition variables for poppers to wait on when the queue is empty */
47 pthread_cond_t queuePopCond;
48 /* Indicates if the queue is shutting down */
53 Work thread for the thread pool.
54 Waits for jobs and executes them.
55 @returns : NULL on failure else non-null.
57 static void* POOL_thread(void* opaque) {
58 POOL_ctx* const ctx = (POOL_ctx*)opaque;
59 if (!ctx) { return NULL; }
61 /* Lock the mutex and wait for a non-empty queue or until shutdown */
62 pthread_mutex_lock(&ctx->queueMutex);
63 while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) {
64 pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex);
66 /* empty => shutting down: so stop */
67 if (ctx->queueHead == ctx->queueTail) {
68 pthread_mutex_unlock(&ctx->queueMutex);
71 /* Pop a job off the queue */
72 { POOL_job const job = ctx->queue[ctx->queueHead];
73 ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;
74 /* Unlock the mutex, signal a pusher, and run the job */
75 pthread_mutex_unlock(&ctx->queueMutex);
76 pthread_cond_signal(&ctx->queuePushCond);
77 job.function(job.opaque);
83 POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
85 /* Check the parameters */
86 if (!numThreads || !queueSize) { return NULL; }
87 /* Allocate the context and zero initialize */
88 ctx = (POOL_ctx *)calloc(1, sizeof(POOL_ctx));
89 if (!ctx) { return NULL; }
90 /* Initialize the job queue.
91 * It needs one extra space since one space is wasted to differentiate empty
94 ctx->queueSize = queueSize + 1;
95 ctx->queue = (POOL_job *)malloc(ctx->queueSize * sizeof(POOL_job));
98 pthread_mutex_init(&ctx->queueMutex, NULL);
99 pthread_cond_init(&ctx->queuePushCond, NULL);
100 pthread_cond_init(&ctx->queuePopCond, NULL);
102 /* Allocate space for the thread handles */
103 ctx->threads = (pthread_t *)malloc(numThreads * sizeof(pthread_t));
105 /* Check for errors */
106 if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; }
107 /* Initialize the threads */
109 for (i = 0; i < numThreads; ++i) {
110 if (pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) {
115 ctx->numThreads = numThreads;
121 Shutdown the queue, wake any sleeping threads, and join all of the threads.
123 static void POOL_join(POOL_ctx *ctx) {
124 /* Shut down the queue */
125 pthread_mutex_lock(&ctx->queueMutex);
127 pthread_mutex_unlock(&ctx->queueMutex);
128 /* Wake up sleeping threads */
129 pthread_cond_broadcast(&ctx->queuePushCond);
130 pthread_cond_broadcast(&ctx->queuePopCond);
131 /* Join all of the threads */
133 for (i = 0; i < ctx->numThreads; ++i) {
134 pthread_join(ctx->threads[i], NULL);
138 void POOL_free(POOL_ctx *ctx) {
139 if (!ctx) { return; }
141 pthread_mutex_destroy(&ctx->queueMutex);
142 pthread_cond_destroy(&ctx->queuePushCond);
143 pthread_cond_destroy(&ctx->queuePopCond);
144 if (ctx->queue) free(ctx->queue);
145 if (ctx->threads) free(ctx->threads);
149 void POOL_add(void *ctxVoid, POOL_function function, void *opaque) {
150 POOL_ctx *ctx = (POOL_ctx *)ctxVoid;
151 if (!ctx) { return; }
153 pthread_mutex_lock(&ctx->queueMutex);
154 { POOL_job const job = {function, opaque};
155 /* Wait until there is space in the queue for the new job */
156 size_t newTail = (ctx->queueTail + 1) % ctx->queueSize;
157 while (ctx->queueHead == newTail && !ctx->shutdown) {
158 pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
159 newTail = (ctx->queueTail + 1) % ctx->queueSize;
161 /* The queue is still going => there is space */
162 if (!ctx->shutdown) {
163 ctx->queue[ctx->queueTail] = job;
164 ctx->queueTail = newTail;
167 pthread_mutex_unlock(&ctx->queueMutex);
168 pthread_cond_signal(&ctx->queuePopCond);
171 #else /* ZSTD_MULTITHREAD not defined */
172 /* No multi-threading support */
174 /* We don't need any data, but if it is empty malloc() might return NULL. */
179 POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) {
182 return (POOL_ctx *)malloc(sizeof(POOL_ctx));
185 void POOL_free(POOL_ctx *ctx) {
189 void POOL_add(void *ctx, POOL_function function, void *opaque) {
194 #endif /* ZSTD_MULTITHREAD */