Path: blob/main/sys/contrib/zstd/lib/common/pool.c
48375 views
/*1* Copyright (c) Yann Collet, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under both the BSD-style license (found in the5* LICENSE file in the root directory of this source tree) and the GPLv2 (found6* in the COPYING file in the root directory of this source tree).7* You may select, at your option, one of the above-listed licenses.8*/91011/* ====== Dependencies ======= */12#include "zstd_deps.h" /* size_t */13#include "debug.h" /* assert */14#include "zstd_internal.h" /* ZSTD_customMalloc, ZSTD_customFree */15#include "pool.h"1617/* ====== Compiler specifics ====== */18#if defined(_MSC_VER)19# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */20#endif212223#ifdef ZSTD_MULTITHREAD2425#include "threading.h" /* pthread adaptation */2627/* A job is a function and an opaque argument */28typedef struct POOL_job_s {29POOL_function function;30void *opaque;31} POOL_job;3233struct POOL_ctx_s {34ZSTD_customMem customMem;35/* Keep track of the threads */36ZSTD_pthread_t* threads;37size_t threadCapacity;38size_t threadLimit;3940/* The queue is a circular buffer */41POOL_job *queue;42size_t queueHead;43size_t queueTail;44size_t queueSize;4546/* The number of threads working on jobs */47size_t numThreadsBusy;48/* Indicates if the queue is empty */49int queueEmpty;5051/* The mutex protects the queue */52ZSTD_pthread_mutex_t queueMutex;53/* Condition variable for pushers to wait on when the queue is full */54ZSTD_pthread_cond_t queuePushCond;55/* Condition variables for poppers to wait on when the queue is empty */56ZSTD_pthread_cond_t queuePopCond;57/* Indicates if the queue is shutting down */58int shutdown;59};6061/* POOL_thread() :62* Work thread for the thread pool.63* Waits for jobs and executes them.64* @returns : NULL on failure else non-null.65*/66static void* POOL_thread(void* opaque) {67POOL_ctx* const ctx = (POOL_ctx*)opaque;68if (!ctx) { return NULL; }69for (;;) {70/* Lock the mutex and wait for a non-empty queue or until shutdown */71ZSTD_pthread_mutex_lock(&ctx->queueMutex);7273while ( ctx->queueEmpty74|| (ctx->numThreadsBusy >= ctx->threadLimit) ) {75if (ctx->shutdown) {76/* even if !queueEmpty, (possible if numThreadsBusy >= threadLimit),77* a few threads will be shutdown while !queueEmpty,78* but enough threads will remain active to finish the queue */79ZSTD_pthread_mutex_unlock(&ctx->queueMutex);80return opaque;81}82ZSTD_pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex);83}84/* Pop a job off the queue */85{ POOL_job const job = ctx->queue[ctx->queueHead];86ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;87ctx->numThreadsBusy++;88ctx->queueEmpty = (ctx->queueHead == ctx->queueTail);89/* Unlock the mutex, signal a pusher, and run the job */90ZSTD_pthread_cond_signal(&ctx->queuePushCond);91ZSTD_pthread_mutex_unlock(&ctx->queueMutex);9293job.function(job.opaque);9495/* If the intended queue size was 0, signal after finishing job */96ZSTD_pthread_mutex_lock(&ctx->queueMutex);97ctx->numThreadsBusy--;98if (ctx->queueSize == 1) {99ZSTD_pthread_cond_signal(&ctx->queuePushCond);100}101ZSTD_pthread_mutex_unlock(&ctx->queueMutex);102}103} /* for (;;) */104assert(0); /* Unreachable */105}106107/* ZSTD_createThreadPool() : public access point */108POOL_ctx* ZSTD_createThreadPool(size_t numThreads) {109return POOL_create (numThreads, 0);110}111112POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {113return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);114}115116POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize,117ZSTD_customMem customMem)118{119POOL_ctx* ctx;120/* Check parameters */121if (!numThreads) { return NULL; }122/* Allocate the context and zero initialize */123ctx = (POOL_ctx*)ZSTD_customCalloc(sizeof(POOL_ctx), customMem);124if (!ctx) { return NULL; }125/* Initialize the job queue.126* It needs one extra space since one space is wasted to differentiate127* empty and full queues.128*/129ctx->queueSize = queueSize + 1;130ctx->queue = (POOL_job*)ZSTD_customMalloc(ctx->queueSize * sizeof(POOL_job), customMem);131ctx->queueHead = 0;132ctx->queueTail = 0;133ctx->numThreadsBusy = 0;134ctx->queueEmpty = 1;135{136int error = 0;137error |= ZSTD_pthread_mutex_init(&ctx->queueMutex, NULL);138error |= ZSTD_pthread_cond_init(&ctx->queuePushCond, NULL);139error |= ZSTD_pthread_cond_init(&ctx->queuePopCond, NULL);140if (error) { POOL_free(ctx); return NULL; }141}142ctx->shutdown = 0;143/* Allocate space for the thread handles */144ctx->threads = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), customMem);145ctx->threadCapacity = 0;146ctx->customMem = customMem;147/* Check for errors */148if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; }149/* Initialize the threads */150{ size_t i;151for (i = 0; i < numThreads; ++i) {152if (ZSTD_pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) {153ctx->threadCapacity = i;154POOL_free(ctx);155return NULL;156} }157ctx->threadCapacity = numThreads;158ctx->threadLimit = numThreads;159}160return ctx;161}162163/*! POOL_join() :164Shutdown the queue, wake any sleeping threads, and join all of the threads.165*/166static void POOL_join(POOL_ctx* ctx) {167/* Shut down the queue */168ZSTD_pthread_mutex_lock(&ctx->queueMutex);169ctx->shutdown = 1;170ZSTD_pthread_mutex_unlock(&ctx->queueMutex);171/* Wake up sleeping threads */172ZSTD_pthread_cond_broadcast(&ctx->queuePushCond);173ZSTD_pthread_cond_broadcast(&ctx->queuePopCond);174/* Join all of the threads */175{ size_t i;176for (i = 0; i < ctx->threadCapacity; ++i) {177ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */178} }179}180181void POOL_free(POOL_ctx *ctx) {182if (!ctx) { return; }183POOL_join(ctx);184ZSTD_pthread_mutex_destroy(&ctx->queueMutex);185ZSTD_pthread_cond_destroy(&ctx->queuePushCond);186ZSTD_pthread_cond_destroy(&ctx->queuePopCond);187ZSTD_customFree(ctx->queue, ctx->customMem);188ZSTD_customFree(ctx->threads, ctx->customMem);189ZSTD_customFree(ctx, ctx->customMem);190}191192void ZSTD_freeThreadPool (ZSTD_threadPool* pool) {193POOL_free (pool);194}195196size_t POOL_sizeof(const POOL_ctx* ctx) {197if (ctx==NULL) return 0; /* supports sizeof NULL */198return sizeof(*ctx)199+ ctx->queueSize * sizeof(POOL_job)200+ ctx->threadCapacity * sizeof(ZSTD_pthread_t);201}202203204/* @return : 0 on success, 1 on error */205static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads)206{207if (numThreads <= ctx->threadCapacity) {208if (!numThreads) return 1;209ctx->threadLimit = numThreads;210return 0;211}212/* numThreads > threadCapacity */213{ ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);214if (!threadPool) return 1;215/* replace existing thread pool */216ZSTD_memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool));217ZSTD_customFree(ctx->threads, ctx->customMem);218ctx->threads = threadPool;219/* Initialize additional threads */220{ size_t threadId;221for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) {222if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) {223ctx->threadCapacity = threadId;224return 1;225} }226} }227/* successfully expanded */228ctx->threadCapacity = numThreads;229ctx->threadLimit = numThreads;230return 0;231}232233/* @return : 0 on success, 1 on error */234int POOL_resize(POOL_ctx* ctx, size_t numThreads)235{236int result;237if (ctx==NULL) return 1;238ZSTD_pthread_mutex_lock(&ctx->queueMutex);239result = POOL_resize_internal(ctx, numThreads);240ZSTD_pthread_cond_broadcast(&ctx->queuePopCond);241ZSTD_pthread_mutex_unlock(&ctx->queueMutex);242return result;243}244245/**246* Returns 1 if the queue is full and 0 otherwise.247*248* When queueSize is 1 (pool was created with an intended queueSize of 0),249* then a queue is empty if there is a thread free _and_ no job is waiting.250*/251static int isQueueFull(POOL_ctx const* ctx) {252if (ctx->queueSize > 1) {253return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize);254} else {255return (ctx->numThreadsBusy == ctx->threadLimit) ||256!ctx->queueEmpty;257}258}259260261static void262POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)263{264POOL_job const job = {function, opaque};265assert(ctx != NULL);266if (ctx->shutdown) return;267268ctx->queueEmpty = 0;269ctx->queue[ctx->queueTail] = job;270ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;271ZSTD_pthread_cond_signal(&ctx->queuePopCond);272}273274void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque)275{276assert(ctx != NULL);277ZSTD_pthread_mutex_lock(&ctx->queueMutex);278/* Wait until there is space in the queue for the new job */279while (isQueueFull(ctx) && (!ctx->shutdown)) {280ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);281}282POOL_add_internal(ctx, function, opaque);283ZSTD_pthread_mutex_unlock(&ctx->queueMutex);284}285286287int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque)288{289assert(ctx != NULL);290ZSTD_pthread_mutex_lock(&ctx->queueMutex);291if (isQueueFull(ctx)) {292ZSTD_pthread_mutex_unlock(&ctx->queueMutex);293return 0;294}295POOL_add_internal(ctx, function, opaque);296ZSTD_pthread_mutex_unlock(&ctx->queueMutex);297return 1;298}299300301#else /* ZSTD_MULTITHREAD not defined */302303/* ========================== */304/* No multi-threading support */305/* ========================== */306307308/* We don't need any data, but if it is empty, malloc() might return NULL. */309struct POOL_ctx_s {310int dummy;311};312static POOL_ctx g_poolCtx;313314POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {315return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);316}317318POOL_ctx*319POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem)320{321(void)numThreads;322(void)queueSize;323(void)customMem;324return &g_poolCtx;325}326327void POOL_free(POOL_ctx* ctx) {328assert(!ctx || ctx == &g_poolCtx);329(void)ctx;330}331332int POOL_resize(POOL_ctx* ctx, size_t numThreads) {333(void)ctx; (void)numThreads;334return 0;335}336337void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) {338(void)ctx;339function(opaque);340}341342int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) {343(void)ctx;344function(opaque);345return 1;346}347348size_t POOL_sizeof(const POOL_ctx* ctx) {349if (ctx==NULL) return 0; /* supports sizeof NULL */350assert(ctx == &g_poolCtx);351return sizeof(*ctx);352}353354#endif /* ZSTD_MULTITHREAD */355356357