├── .gitignore ├── Makefile ├── README.md ├── example ├── Makefile ├── env_thread_test.cc └── threadpool_test.cc ├── include ├── env.h ├── threadpool.h └── threadpool_imp.h └── src ├── env.cc ├── env_posix.cc └── threadpool_imp.cc /.gitignore: -------------------------------------------------------------------------------- 1 | make_config.mk 2 | 3 | *.a 4 | *.arc 5 | *.d 6 | *.dylib* 7 | *.gcda 8 | *.gcno 9 | *.o 10 | *.so 11 | *.so.* 12 | *_test 13 | *_bench 14 | *_stress 15 | *.out 16 | *.class 17 | *.jar 18 | *.*jnilib* 19 | *.d-e 20 | *.o-* 21 | *.swp 22 | *.dSYM 23 | *~ 24 | *.vcxproj 25 | *.vcxproj.filters 26 | *.sln 27 | *.cmake 28 | .watchmanconfig 29 | CMakeCache.txt 30 | CMakeFiles/ 31 | build/ 32 | 33 | .gdbhistory 34 | .gdb_history 35 | tags 36 | etags 37 | .DS_Store 38 | 39 | java/out 40 | java/target 41 | java/test-libs 42 | java/*.log 43 | 44 | .idea/ 45 | *.iml 46 | 47 | t 48 | LOG 49 | 50 | tp2/ 51 | fbcode/ 52 | fbcode 53 | buckifier/*.pyc 54 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # step 1: 2 | # prepare the compiler 3 | # prepare targe file name 4 | # prepare dependecy libarary 5 | CC = g++ 6 | CFLAGS := -Wall -O2 -fPIC -std=c++11 7 | LDFLAGS = -lpthread 8 | 9 | # header file's path 10 | INCLUDE_PATH = -I ./include 11 | SRC_PATH = ./src/ 12 | 13 | # .o file with the same name of .ccc file 14 | OBJS = threadpool_imp.o env_posix.o env.o 15 | SRCS = $(SRC_PATH)threadpool_imp.cc $(SRC_PATH)env_posix.cc $(SRC_PATH)env.cc 16 | TARGET = libthreadpool.so 17 | STATIC_TARGET = libthreadpool.a 18 | 19 | # step 2: 20 | # produce the .o files 21 | $(OBJS):$(SRCS) 22 | $(CC) $(CFLAGS) $(INCLUDE_PATH) -c $^ 23 | 24 | # 3. produce the .so file 25 | # 'ar crv' produce static lib 26 | # 'cc - shared' produce shared lib 27 | all : $(OBJS) 28 | ar crv $(STATIC_TARGET) $(OBJS) 29 | $(CC) -shared $(CFLAGS) $(INCLUDE_PATH) -o $(TARGET) $(OBJS) $(LDFLAGS) 30 | 31 | # 4. clean the files except source file 32 | clean: 33 | rm -f $(OBJS) $(LIB) $(TARGET) $(STATIC_TARGET) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | This is a ThreadPool with C++ language. 3 | 4 | Support some features: 5 | - Maintain a pool to automatic create,run,destroy a thread. 6 | - Dynamic increase/descrease the thread pool's thread nums 7 | - Support adjust the thread's CPU and I/O priority 8 | - Expand your environment with inherit `Class Env` 9 | 10 | There is a chinese blog to describe the implementation of this threadpool which is written by me. 11 | [A industrial threadpool's implementation](https://blog.csdn.net/Z_Stand/article/details/114156801?spm=1001.2014.3001.5502) 12 | 13 | ## Compile 14 | > compile 15 | > os: centos7.3.1611 16 | > gcc: 5.3 17 | 18 | - for lib and static lib: `make all` 19 | - for example test: 20 | ```c 21 | cd example 22 | make all 23 | ``` 24 | > Before you compile the example, the libthreadpool.so must be produced 25 | > in the first step. 26 | 27 | ## API 28 | ### Create ThreadPool 29 | ```c++ 30 | auto* thread_pool = new ThreadPoolImpl(); 31 | ``` 32 | The interface will produce a PosixEnv to init multi threadpools with LOW/HIGH/USER .etc. 33 | 34 | ### Schedule thread 35 | ```c 36 | void Schedule(void (*function)(void* arg1), void* arg, Priority pri = LOW, 37 | void* tag = nullptr, 38 | void (*unschedFunction)(void* arg) = nullptr) override; 39 | ``` 40 | 41 | - `function` is thread func 42 | - `arg` is thread func's args, it's better to choose a struct 43 | - `pri` Env support seperate threads with different tag 44 | - `LOW` 45 | - `HIGH` 46 | 47 | With these tags, you can set a convinient cpu or I/O priority to these threads 48 | 49 | ### Priority thread 50 | ```c 51 | void LowerThreadPoolIOPriority(Priority pool = LOW); 52 | ``` 53 | 54 | ### Set threads' number limits 55 | ```c 56 | void SetBackgroundThreads(int num, Priority pri); 57 | ``` 58 | You can set the threads' num with identity tag, eg: `env->SetBackgroundThreads(10, Env::Priority::LOW)` 59 | Means that the 'LOW' threadpool has less than 10 threads. 60 | 61 | 62 | ### Example 63 | For the detail ,you can see the `example/threadpool_test.cc` 64 | 65 | ```c 66 | std::atomic last_id(0); 67 | 68 | auto* thread_pool1 = new ThreadPoolImpl(); 69 | auto* thread_pool2 = new ThreadPoolImpl(); 70 | 71 | // Set the background threads in threadpool 72 | // threadpool1 have 3 threads and with lower IO priority 73 | // threadpool2 have 7 threads and with lower CPU priority 74 | thread_pool1->SetBackgroundThreads(3); 75 | thread_pool2->SetBackgroundThreads(7); 76 | 77 | thread_pool1->LowerIOPriority(); 78 | thread_pool2->LowerCPUPriority(); 79 | 80 | for (int i = 0, j = 0;i < 10; j++,i ++) { 81 | Cxt cxt_i(&last_id, i); 82 | Cxt cxt_j(&last_id, j); 83 | if (i % 2 == 0 ) { 84 | thread_pool1->Schedule(&Thread1, &cxt_i, &cxt_i, &finish1); 85 | } else { 86 | thread_pool2->Schedule(&Thread2, &cxt_j, &cxt_j, &finish1); 87 | } 88 | } 89 | 90 | Cxt cxt_us(&last_id, 1); 91 | thread_pool1->UnSchedule(&cxt_us); 92 | thread_pool2->UnSchedule(&cxt_us); 93 | ``` 94 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | # step 1: 2 | # prepare the compiler 3 | # prepare targe file name 4 | # prepare dependecy libarary 5 | CC = g++ 6 | CFLAGS := -Wall -O2 -fPIC -std=c++11 7 | LDFLAGS = -lpthread 8 | 9 | # ifneq ($(USE_RTTI), 1) 10 | # CFLAGS += -fno-rtti 11 | # endif 12 | 13 | # header file's path 14 | INCLUDE_PATH = -I ../include 15 | LIBPATH = ../libthreadpool.so 16 | STATIC_LIBPATH = ../libthreadpool.a 17 | 18 | all : threadpool_test env_thread_test 19 | 20 | threadpool_test: threadpool_test.cc 21 | $(CC) $(CFLAGS) $(INCLUDE_PATH) $(LIBPATH) $@.cc -o$@ $(LDFLAGS) 22 | 23 | env_thread_test: env_thread_test.cc 24 | $(CC) $(CFLAGS) $(INCLUDE_PATH) $(LIBPATH) $@.cc -o$@ $(LDFLAGS) 25 | 26 | # 4. clean the files except source file 27 | clean: 28 | rm -f ./threadpool_test ./env_thread_test 29 | -------------------------------------------------------------------------------- /example/env_thread_test.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhanghuigui on 2021/3/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "env.h" 10 | #include "threadpool.h" 11 | 12 | #define THREAD_COUNT 10 13 | 14 | using std::cout; 15 | using std::endl; 16 | 17 | struct Cxt{ 18 | int thread_id; 19 | std::atomic* last_id; 20 | 21 | Cxt(std::atomic* p, int i) : 22 | thread_id(i),last_id(p) {} 23 | }; 24 | 25 | static void Thread1(void *ptr) { 26 | Cxt *cxt = reinterpret_cast(ptr); 27 | int count = THREAD_COUNT; 28 | while(count --) { 29 | printf("------- *%d* running ------- \n", cxt->thread_id); 30 | sleep(2); 31 | } 32 | } 33 | 34 | static void Thread2(void *ptr) { 35 | Cxt *cxt = reinterpret_cast(ptr); 36 | int count = THREAD_COUNT; 37 | while(count --) { 38 | printf("------- *%d* running ------- \n", cxt->thread_id); 39 | sleep(2); 40 | } 41 | } 42 | 43 | static void finish1(void *ptr) { 44 | Cxt *cxt = reinterpret_cast(ptr); 45 | printf("Finish excute %d\n", cxt->thread_id); 46 | delete cxt; 47 | } 48 | 49 | void PrintEnvInfo(Env *env) { 50 | if (env == nullptr) { 51 | return; 52 | } 53 | 54 | int low_thread_nums; 55 | int high_thread_nums; 56 | uint64_t time; 57 | 58 | time = env->NowMicros(); 59 | low_thread_nums = env->GetThreadPoolQueueLen(Env::Priority::LOW); 60 | high_thread_nums = env->GetThreadPoolQueueLen(Env::Priority::HIGH); 61 | 62 | cout << "time : " << env->TimeToString(time) << endl 63 | << "low thread nums: " << low_thread_nums << endl 64 | << "high thread nums: " << high_thread_nums << endl 65 | << "thread id: " << env->GetThreadID() << endl 66 | << endl; 67 | 68 | } 69 | 70 | int main(int argc, char *argv[]) { 71 | Env *env = Env::Default(); 72 | std::atomic last_id(0); 73 | 74 | env->SetBackgroundThreads(3, Env::Priority::LOW); 75 | env->SetBackgroundThreads(7, Env::Priority::HIGH); 76 | 77 | for (int i = 0, j = 0;i < 10; j++,i ++) { 78 | Cxt cxt_i(&last_id, i); 79 | Cxt cxt_j(&last_id, j); 80 | if (i % 2 == 0 ) { 81 | env->Schedule(&Thread1, &cxt_i, Env::Priority::LOW, &cxt_i, &finish1); 82 | } else { 83 | env->Schedule(&Thread2, &cxt_j, Env::Priority::HIGH, &cxt_j, &finish1); 84 | } 85 | 86 | PrintEnvInfo(env); 87 | } 88 | 89 | Cxt cxt_us(&last_id, 1); 90 | env->UnSchedule(&cxt_us, Env::Priority::LOW); 91 | 92 | return 0; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /example/threadpool_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "threadpool.h" 6 | #include "threadpool_imp.h" 7 | 8 | #define THREAD_COUNT 10 9 | 10 | using std::cout; 11 | using std::endl; 12 | 13 | struct Cxt{ 14 | int thread_id; 15 | std::atomic* last_id; 16 | 17 | Cxt(std::atomic* p, int i) : 18 | thread_id(i),last_id(p) {} 19 | }; 20 | 21 | static void Thread1(void *ptr) { 22 | Cxt *cxt = reinterpret_cast(ptr); 23 | int count = THREAD_COUNT; 24 | while(count --) { 25 | printf("------- *%d* running ------- \n", cxt->thread_id); 26 | sleep(2); 27 | } 28 | } 29 | 30 | static void Thread2(void *ptr) { 31 | Cxt *cxt = reinterpret_cast(ptr); 32 | int count = THREAD_COUNT; 33 | while(count --) { 34 | printf("------- *%d* running ------- \n", cxt->thread_id); 35 | sleep(2); 36 | } 37 | } 38 | 39 | static void finish1(void *ptr) { 40 | Cxt *cxt = reinterpret_cast(ptr); 41 | printf("Finish excute %d\n", cxt->thread_id); 42 | delete cxt; 43 | } 44 | 45 | int main(int argc, char *argv[]) { 46 | std::atomic last_id(0); 47 | 48 | auto* thread_pool1 = new ThreadPoolImpl(); 49 | auto* thread_pool2 = new ThreadPoolImpl(); 50 | 51 | // Set the background threads in threadpool 52 | // threadpool1 have 3 threads and with lower IO priority 53 | // threadpool2 have 7 threads and with lower CPU priority 54 | thread_pool1->SetBackgroundThreads(3); 55 | thread_pool2->SetBackgroundThreads(7); 56 | 57 | thread_pool1->LowerIOPriority(); 58 | thread_pool2->LowerCPUPriority(); 59 | 60 | for (int i = 0, j = 0;i < 10; j++,i ++) { 61 | Cxt cxt_i(&last_id, i); 62 | Cxt cxt_j(&last_id, j); 63 | if (i % 2 == 0 ) { 64 | thread_pool1->Schedule(&Thread1, &cxt_i, &cxt_i, &finish1); 65 | } else { 66 | thread_pool2->Schedule(&Thread2, &cxt_j, &cxt_j, &finish1); 67 | } 68 | } 69 | 70 | Cxt cxt_us(&last_id, 1); 71 | thread_pool1->UnSchedule(&cxt_us); 72 | thread_pool2->UnSchedule(&cxt_us); 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /include/env.h: -------------------------------------------------------------------------------- 1 | // An Env for wrap threadpool's API. The Env was the rocksdb's environment. 2 | // After that you can define your own env class, just inherit the Env class 3 | // and implementation the interface you want to use. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class Env { 16 | public: 17 | 18 | Env() = default; 19 | 20 | virtual ~Env(); 21 | 22 | static const char* Type() { return "Environment"; } 23 | 24 | // Return a default environment suitable for the current operating 25 | // system. Sophisticated users may wish to provide their own Env 26 | // implementation instead of relying on this default environment. 27 | // 28 | // The result of Default() belongs to rocksdb and must never be deleted. 29 | static Env* Default(); 30 | 31 | // Priority for scheduling job in thread pool 32 | enum Priority { BOTTOM, LOW, HIGH, USER, TOTAL }; 33 | 34 | // Priority for requesting bytes in rate limiter scheduler 35 | enum IOPriority { IO_LOW = 0, IO_HIGH = 1, IO_TOTAL = 2 }; 36 | 37 | // Arrange to run "(*function)(arg)" once in a background thread, in 38 | // the thread pool specified by pri. By default, jobs go to the 'LOW' 39 | // priority thread pool. 40 | 41 | // "function" may run in an unspecified thread. Multiple functions 42 | // added to the same Env may run concurrently in different threads. 43 | // I.e., the caller may not assume that background work items are 44 | // serialized. 45 | // When the UnSchedule function is called, the unschedFunction 46 | // registered at the time of Schedule is invoked with arg as a parameter. 47 | virtual void Schedule(void (*function)(void* arg), void* arg, 48 | Priority pri = LOW, void* tag = nullptr, 49 | void (*unschedFunction)(void* arg) = nullptr) = 0; 50 | 51 | // Arrange to remove jobs for given arg from the queue_ if they are not 52 | // already scheduled. Caller is expected to have exclusive lock on arg. 53 | virtual int UnSchedule(void* /*arg*/, Priority /*pri*/) { return 0; } 54 | 55 | // Start a new thread, invoking "function(arg)" within the new thread. 56 | // When "function(arg)" returns, the thread will be destroyed. 57 | virtual void StartThread(void (*function)(void* arg), void* arg) = 0; 58 | 59 | // Wait for all threads started by StartThread to terminate. 60 | virtual void WaitForJoin() {} 61 | 62 | // Get thread pool queue length for specific thread pool. 63 | virtual unsigned int GetThreadPoolQueueLen(Priority /*pri*/ = LOW) const { 64 | return 0; 65 | } 66 | 67 | // The number of background worker threads of a specific thread pool 68 | // for this environment. 'LOW' is the default pool. 69 | // default number: 1 70 | virtual void SetBackgroundThreads(int number, Priority pri = LOW) = 0; 71 | virtual int GetBackgroundThreads(Priority pri = LOW) = 0; 72 | 73 | // Enlarge number of background worker threads of a specific thread pool 74 | // for this environment if it is smaller than specified. 'LOW' is the default 75 | // pool. 76 | virtual void IncBackgroundThreadsIfNeeded(int number, Priority pri) = 0; 77 | 78 | // Lower IO priority for threads from the specified pool. 79 | virtual void LowerThreadPoolIOPriority(Priority /*pool*/ = LOW) {} 80 | 81 | // Lower CPU priority for threads from the specified pool. 82 | virtual void LowerThreadPoolCPUPriority(Priority /*pool*/ = LOW) {} 83 | 84 | // Converts seconds-since-Jan-01-1970 to a printable string 85 | virtual std::string TimeToString(uint64_t time) = 0; 86 | 87 | virtual uint64_t NowMicros() = 0; 88 | 89 | virtual uint64_t NowNanos() = 0; 90 | 91 | virtual uint64_t NowCPUNanos() = 0; 92 | 93 | // Returns the ID of the current thread. 94 | virtual uint64_t GetThreadID() const = 0; 95 | 96 | // This seems to clash with a macro on Windows, so #undef it here 97 | #undef GetFreeSpace 98 | 99 | private: 100 | // No copying allowed 101 | Env(const Env&); 102 | void operator=(const Env&); 103 | }; 104 | 105 | // An implementation of Env that forwards all calls to another Env. 106 | // May be useful to clients who wish to override just part of the 107 | // functionality of another Env. 108 | class EnvWrapper : public Env { 109 | public: 110 | // Initialize an EnvWrapper that delegates all calls to *t 111 | explicit EnvWrapper(Env* t) : target_(t) {} 112 | ~EnvWrapper(); 113 | 114 | // Return the target to which this Env forwards all calls 115 | Env* target() const { return target_; } 116 | 117 | void Schedule(void (*f)(void* arg), void* a, Priority pri, 118 | void* tag = nullptr, void (*u)(void* arg) = nullptr) override { 119 | return target_->Schedule(f, a, pri, tag, u); 120 | } 121 | 122 | int UnSchedule(void* tag, Priority pri) override { 123 | return target_->UnSchedule(tag, pri); 124 | } 125 | 126 | void StartThread(void (*f)(void*), void* a) override { 127 | return target_->StartThread(f, a); 128 | } 129 | 130 | void WaitForJoin() override { return target_->WaitForJoin(); } 131 | unsigned int GetThreadPoolQueueLen(Priority pri = LOW) const override { 132 | return target_->GetThreadPoolQueueLen(pri); 133 | } 134 | 135 | void SetBackgroundThreads(int num, Priority pri) override { 136 | return target_->SetBackgroundThreads(num, pri); 137 | } 138 | int GetBackgroundThreads(Priority pri) override { 139 | return target_->GetBackgroundThreads(pri); 140 | } 141 | 142 | void IncBackgroundThreadsIfNeeded(int num, Priority pri) override { 143 | return target_->IncBackgroundThreadsIfNeeded(num, pri); 144 | } 145 | 146 | void LowerThreadPoolIOPriority(Priority pool = LOW) override { 147 | target_->LowerThreadPoolIOPriority(pool); 148 | } 149 | 150 | void LowerThreadPoolCPUPriority(Priority pool = LOW) override { 151 | target_->LowerThreadPoolCPUPriority(pool); 152 | } 153 | 154 | uint64_t NowMicros() override { 155 | return target_->NowMicros(); 156 | } 157 | 158 | uint64_t NowNanos() override { 159 | return target_->NowNanos(); 160 | } 161 | 162 | uint64_t NowCPUNanos() override { 163 | return target_->NowCPUNanos(); 164 | } 165 | 166 | std::string TimeToString(uint64_t time) override { 167 | return target_->TimeToString(time); 168 | } 169 | 170 | uint64_t GetThreadID() const override { return target_->GetThreadID(); } 171 | 172 | private: 173 | Env* target_; 174 | }; 175 | 176 | -------------------------------------------------------------------------------- /include/threadpool.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 | // This source code is licensed under both the GPLv2 (found in the 3 | // COPYING file in the root directory) and Apache 2.0 License 4 | // (found in the LICENSE.Apache file in the root directory). 5 | // 6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 | // Use of this source code is governed by a BSD-style license that can be 8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 | #pragma once 10 | 11 | #include 12 | 13 | /* 14 | * ThreadPool is a component that will spawn N background threads that will 15 | * be used to execute scheduled work, The number of background threads could 16 | * be modified by calling SetBackgroundThreads(). 17 | * */ 18 | class ThreadPool { 19 | public: 20 | virtual ~ThreadPool() {} 21 | 22 | // Wait for all threads to finish. 23 | // Discard those threads that did not start 24 | // executing 25 | virtual void JoinAllThreads() = 0; 26 | 27 | // Set the number of background threads that will be executing the 28 | // scheduled jobs. 29 | virtual void SetBackgroundThreads(int num) = 0; 30 | virtual int GetBackgroundThreads() = 0; 31 | 32 | // Get the number of jobs scheduled in the ThreadPool queue. 33 | virtual unsigned int GetQueueLen() const = 0; 34 | 35 | // Waits for all jobs to complete those 36 | // that already started running and those that did not 37 | // start yet. This ensures that everything that was thrown 38 | // on the TP runs even though 39 | // we may not have specified enough threads for the amount 40 | // of jobs 41 | virtual void WaitForJobsAndJoinAllThreads() = 0; 42 | 43 | // Submit a fire and forget jobs 44 | // This allows to submit the same job multiple times 45 | virtual void SubmitJob(const std::function&) = 0; 46 | // This moves the function in for efficiency 47 | virtual void SubmitJob(std::function&&) = 0; 48 | }; 49 | 50 | // NewThreadPool() is a function that could be used to create a ThreadPool 51 | // with `num_threads` background threads. 52 | extern ThreadPool* NewThreadPool(int num_threads); 53 | 54 | -------------------------------------------------------------------------------- /include/threadpool_imp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 | // This source code is licensed under both the GPLv2 (found in the 3 | // COPYING file in the root directory) and Apache 2.0 License 4 | // (found in the LICENSE.Apache file in the root directory). 5 | // 6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 | // Use of this source code is governed by a BSD-style license that can be 8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 | #pragma once 10 | 11 | #include "threadpool.h" 12 | #include "env.h" 13 | 14 | #include 15 | #include 16 | 17 | 18 | class ThreadPoolImpl : public ThreadPool { 19 | public: 20 | ThreadPoolImpl(); 21 | ~ThreadPoolImpl(); 22 | 23 | ThreadPoolImpl(ThreadPoolImpl&&) = delete; 24 | ThreadPoolImpl& operator=(ThreadPoolImpl&&) = delete; 25 | 26 | // Implement ThreadPool interfaces 27 | 28 | // Wait for all threads to finish. 29 | // Discards all the jobs that did not 30 | // start executing and waits for those running 31 | // to complete 32 | void JoinAllThreads() override; 33 | 34 | // Set the number of background threads that will be executing the 35 | // scheduled jobs. 36 | void SetBackgroundThreads(int num) override; 37 | int GetBackgroundThreads() override; 38 | 39 | // Get the number of jobs scheduled in the ThreadPool queue. 40 | unsigned int GetQueueLen() const override; 41 | 42 | // Waits for all jobs to complete those 43 | // that already started running and those that did not 44 | // start yet 45 | void WaitForJobsAndJoinAllThreads() override; 46 | 47 | // Make threads to run at a lower kernel IO priority 48 | // Currently only has effect on Linux 49 | void LowerIOPriority(); 50 | 51 | // Make threads to run at a lower kernel CPU priority 52 | // Currently only has effect on Linux 53 | void LowerCPUPriority(); 54 | 55 | // Ensure there is at aleast num threads in the pool 56 | // but do not kill threads if there are more 57 | void IncBackgroundThreadsIfNeeded(int num); 58 | 59 | // Submit a fire and forget job 60 | // These jobs can not be unscheduled 61 | 62 | // This allows to submit the same job multiple times 63 | void SubmitJob(const std::function&) override; 64 | // This moves the function in for efficiency 65 | void SubmitJob(std::function&&) override; 66 | 67 | // Schedule a job with an unschedule tag and unschedule function 68 | // Can be used to filter and unschedule jobs by a tag 69 | // that are still in the queue and did not start running 70 | void Schedule(void (*function)(void* arg1), void* arg, void* tag, 71 | void (*unschedFunction)(void* arg)); 72 | 73 | // Filter jobs that are still in a queue and match 74 | // the given tag. Remove them from a queue if any 75 | // and for each such job execute an unschedule function 76 | // if such was given at scheduling time. 77 | int UnSchedule(void* tag); 78 | 79 | void SetHostEnv(Env* env); 80 | 81 | Env* GetHostEnv() const; 82 | 83 | // Return the thread priority. 84 | // This would allow its member-thread to know its priority. 85 | Env::Priority GetThreadPriority() const; 86 | 87 | // Set the thread priority. 88 | void SetThreadPriority(Env::Priority priority); 89 | 90 | static void PthreadCall(const char* label, int result); 91 | 92 | struct Impl; 93 | 94 | private: 95 | 96 | // Current public virtual interface does not provide usable 97 | // functionality and thus can not be used internally to 98 | // facade different implementations. 99 | // 100 | // We propose a pimpl idiom in order to easily replace the thread pool impl 101 | // w/o touching the header file but providing a different .cc potentially 102 | // CMake option driven. 103 | // 104 | // Another option is to introduce a Env::MakeThreadPool() virtual interface 105 | // and override the environment. This would require refactoring ThreadPool usage. 106 | // 107 | // We can also combine these two approaches 108 | std::unique_ptr impl_; 109 | }; 110 | 111 | -------------------------------------------------------------------------------- /src/env.cc: -------------------------------------------------------------------------------- 1 | #include "env.h" 2 | 3 | Env::~Env() { 4 | } 5 | 6 | EnvWrapper::~EnvWrapper() { 7 | } -------------------------------------------------------------------------------- /src/env_posix.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 | // This source code is licensed under both the GPLv2 (found in the 3 | // COPYING file in the root directory) and Apache 2.0 License 4 | // (found in the LICENSE.Apache file in the root directory). 5 | // 6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 | // Use of this source code is governed by a BSD-style license that can be 8 | // found in the LICENSE file. See the AUTHORS file for names of contributors 9 | #if defined(OS_LINUX) 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #if defined(OS_LINUX) || defined(OS_SOLARIS) || defined(OS_ANDROID) 19 | #include 20 | #include 21 | #include 22 | #endif 23 | #include 24 | #include 25 | #include 26 | // Get nano time includes 27 | #if defined(OS_LINUX) || defined(OS_FREEBSD) 28 | #elif defined(__MACH__) 29 | #include 30 | #include 31 | #else 32 | #include 33 | #endif 34 | #include 35 | #include 36 | 37 | #include "threadpool_imp.h" 38 | #include "env.h" 39 | 40 | class ThreadPoolEnv : public Env { 41 | public: 42 | ThreadPoolEnv(); 43 | 44 | ~ThreadPoolEnv() override { 45 | for (const auto tid : threads_to_join_) { 46 | pthread_join(tid, nullptr); 47 | } 48 | for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) { 49 | thread_pools_[pool_id].JoinAllThreads(); 50 | } 51 | } 52 | 53 | void Schedule(void (*function)(void* arg1), void* arg, Priority pri = LOW, 54 | void* tag = nullptr, 55 | void (*unschedFunction)(void* arg) = nullptr) override; 56 | 57 | int UnSchedule(void* arg, Priority pri) override; 58 | 59 | void StartThread(void (*function)(void* arg), void* arg) override; 60 | 61 | void WaitForJoin() override; 62 | 63 | unsigned int GetThreadPoolQueueLen(Priority pri = LOW) const override; 64 | 65 | static uint64_t gettid(pthread_t tid) { 66 | uint64_t thread_id = 0; 67 | memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); 68 | return thread_id; 69 | } 70 | 71 | static uint64_t gettid() { 72 | pthread_t tid = pthread_self(); 73 | return gettid(tid); 74 | } 75 | 76 | uint64_t GetThreadID() const override { return gettid(pthread_self()); } 77 | 78 | uint64_t NowMicros() override { 79 | struct timeval tv; 80 | gettimeofday(&tv, nullptr); 81 | return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; 82 | } 83 | 84 | uint64_t NowNanos() override { 85 | #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_AIX) 86 | struct timespec ts; 87 | clock_gettime(CLOCK_MONOTONIC, &ts); 88 | return static_cast(ts.tv_sec) * 1000000000 + ts.tv_nsec; 89 | #elif defined(__MACH__) 90 | clock_serv_t cclock; 91 | mach_timespec_t ts; 92 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); 93 | clock_get_time(cclock, &ts); 94 | mach_port_deallocate(mach_task_self(), cclock); 95 | return static_cast(ts.tv_sec) * 1000000000 + ts.tv_nsec; 96 | #else 97 | return std::chrono::duration_cast( 98 | std::chrono::steady_clock::now().time_since_epoch()).count(); 99 | #endif 100 | } 101 | 102 | uint64_t NowCPUNanos() override { 103 | #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_AIX) || \ 104 | defined(__MACH__) 105 | struct timespec ts; 106 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); 107 | return static_cast(ts.tv_sec) * 1000000000 + ts.tv_nsec; 108 | #endif 109 | return 0; 110 | } 111 | 112 | // Allow increasing the number of worker threads. 113 | void SetBackgroundThreads(int num, Priority pri) override { 114 | assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH); 115 | thread_pools_[pri].SetBackgroundThreads(num); 116 | } 117 | 118 | int GetBackgroundThreads(Priority pri) override { 119 | assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH); 120 | return thread_pools_[pri].GetBackgroundThreads(); 121 | } 122 | 123 | // Allow increasing the number of worker threads. 124 | void IncBackgroundThreadsIfNeeded(int num, Priority pri) override { 125 | assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH); 126 | thread_pools_[pri].IncBackgroundThreadsIfNeeded(num); 127 | } 128 | 129 | void LowerThreadPoolIOPriority(Priority pool = LOW) override { 130 | assert(pool >= Priority::BOTTOM && pool <= Priority::HIGH); 131 | #ifdef OS_LINUX 132 | thread_pools_[pool].LowerIOPriority(); 133 | #else 134 | (void)pool; 135 | #endif 136 | } 137 | 138 | void LowerThreadPoolCPUPriority(Priority pool = LOW) override { 139 | assert(pool >= Priority::BOTTOM && pool <= Priority::HIGH); 140 | #ifdef OS_LINUX 141 | thread_pools_[pool].LowerCPUPriority(); 142 | #else 143 | (void)pool; 144 | #endif 145 | } 146 | 147 | std::string TimeToString(uint64_t secondsSince1970) override { 148 | const time_t seconds = (time_t)secondsSince1970; 149 | struct tm t; 150 | int maxsize = 64; 151 | std::string dummy; 152 | dummy.reserve(maxsize); 153 | dummy.resize(maxsize); 154 | char* p = &dummy[0]; 155 | localtime_r(&seconds, &t); 156 | snprintf(p, maxsize, 157 | "%04d/%02d/%02d-%02d:%02d:%02d ", 158 | t.tm_year + 1900, 159 | t.tm_mon + 1, 160 | t.tm_mday, 161 | t.tm_hour, 162 | t.tm_min, 163 | t.tm_sec); 164 | return dummy; 165 | } 166 | 167 | private: 168 | std::vector thread_pools_; 169 | pthread_mutex_t mu_; 170 | std::vector threads_to_join_; 171 | }; 172 | 173 | ThreadPoolEnv::ThreadPoolEnv() 174 | : thread_pools_(Priority::TOTAL) { 175 | ThreadPoolImpl::PthreadCall("mutex_init", pthread_mutex_init(&mu_, nullptr)); 176 | for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) { 177 | thread_pools_[pool_id].SetThreadPriority( 178 | static_cast(pool_id)); 179 | // This allows later initializing the thread-local-env of each thread. 180 | thread_pools_[pool_id].SetHostEnv(this); 181 | } 182 | } 183 | 184 | void ThreadPoolEnv::Schedule(void (*function)(void* arg1), void* arg, Priority pri, 185 | void* tag, void (*unschedFunction)(void* arg)) { 186 | assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH); 187 | thread_pools_[pri].Schedule(function, arg, tag, unschedFunction); 188 | } 189 | 190 | int ThreadPoolEnv::UnSchedule(void* arg, Priority pri) { 191 | return thread_pools_[pri].UnSchedule(arg); 192 | } 193 | 194 | unsigned int ThreadPoolEnv::GetThreadPoolQueueLen(Priority pri) const { 195 | assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH); 196 | return thread_pools_[pri].GetQueueLen(); 197 | } 198 | 199 | struct StartThreadState { 200 | void (*user_function)(void*); 201 | void* arg; 202 | }; 203 | 204 | static void* StartThreadWrapper(void* arg) { 205 | StartThreadState* state = reinterpret_cast(arg); 206 | state->user_function(state->arg); 207 | delete state; 208 | return nullptr; 209 | } 210 | 211 | void ThreadPoolEnv::StartThread(void (*function)(void* arg), void* arg) { 212 | pthread_t t; 213 | StartThreadState* state = new StartThreadState; 214 | state->user_function = function; 215 | state->arg = arg; 216 | ThreadPoolImpl::PthreadCall( 217 | "start thread", pthread_create(&t, nullptr, &StartThreadWrapper, state)); 218 | ThreadPoolImpl::PthreadCall("lock", pthread_mutex_lock(&mu_)); 219 | threads_to_join_.push_back(t); 220 | ThreadPoolImpl::PthreadCall("unlock", pthread_mutex_unlock(&mu_)); 221 | } 222 | 223 | void ThreadPoolEnv::WaitForJoin() { 224 | for (const auto tid : threads_to_join_) { 225 | pthread_join(tid, nullptr); 226 | } 227 | threads_to_join_.clear(); 228 | } 229 | 230 | // 231 | // Default Posix Env 232 | // 233 | Env* Env::Default() { 234 | // The following function call initializes the singletons of ThreadLocalPtr 235 | // right before the static default_env. This guarantees default_env will 236 | // always being destructed before the ThreadLocalPtr singletons get 237 | // destructed as C++ guarantees that the destructions of static variables 238 | // is in the reverse order of their constructions. 239 | // 240 | // Since static members are destructed in the reverse order 241 | // of their construction, having this call here guarantees that 242 | // the destructor of static ThreadPoolEnv will go first, then the 243 | // the singletons of ThreadLocalPtr. 244 | static ThreadPoolEnv default_env; 245 | return &default_env; 246 | } 247 | 248 | -------------------------------------------------------------------------------- /src/threadpool_imp.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 | // This source code is licensed under both the GPLv2 (found in the 3 | // COPYING file in the root directory) and Apache 2.0 License 4 | // (found in the LICENSE.Apache file in the root directory). 5 | // 6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 | // Use of this source code is governed by a BSD-style license that can be 8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 | 10 | #include "threadpool_imp.h" 11 | 12 | #ifndef OS_WIN 13 | # include 14 | #endif 15 | 16 | #ifdef OS_LINUX 17 | # include 18 | # include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | void ThreadPoolImpl::PthreadCall(const char* label, int result) { 34 | if (result != 0) { 35 | fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); 36 | abort(); 37 | } 38 | } 39 | 40 | struct ThreadPoolImpl::Impl { 41 | 42 | Impl(); 43 | ~Impl(); 44 | 45 | void JoinThreads(bool wait_for_jobs_to_complete); 46 | 47 | void SetBackgroundThreadsInternal(int num, bool allow_reduce); 48 | int GetBackgroundThreads(); 49 | 50 | unsigned int GetQueueLen() const { 51 | return queue_len_.load(std::memory_order_relaxed); 52 | } 53 | 54 | void LowerIOPriority(); 55 | 56 | void LowerCPUPriority(); 57 | 58 | void WakeUpAllThreads() { 59 | bgsignal_.notify_all(); 60 | } 61 | 62 | void BGThread(size_t thread_id); 63 | 64 | void StartBGThreads(); 65 | 66 | void Submit(std::function&& schedule, 67 | std::function&& unschedule, void* tag); 68 | 69 | int UnSchedule(void* arg); 70 | 71 | void SetHostEnv(Env* env) { env_ = env; } 72 | 73 | Env* GetHostEnv() const { return env_; } 74 | 75 | bool HasExcessiveThread() const { 76 | return static_cast(bgthreads_.size()) > total_threads_limit_; 77 | } 78 | 79 | // Return true iff the current thread is the excessive thread to terminate. 80 | // Always terminate the running thread that is added last, even if there are 81 | // more than one thread to terminate. 82 | bool IsLastExcessiveThread(size_t thread_id) const { 83 | return HasExcessiveThread() && thread_id == bgthreads_.size() - 1; 84 | } 85 | 86 | bool IsExcessiveThread(size_t thread_id) const { 87 | return static_cast(thread_id) >= total_threads_limit_; 88 | } 89 | 90 | // Return the thread priority. 91 | // This would allow its member-thread to know its priority. 92 | Env::Priority GetThreadPriority() const { return priority_; } 93 | 94 | // Set the thread priority. 95 | void SetThreadPriority(Env::Priority priority) { priority_ = priority; } 96 | 97 | private: 98 | 99 | static void* BGThreadWrapper(void* arg); 100 | 101 | bool low_io_priority_; 102 | bool low_cpu_priority_; 103 | Env::Priority priority_; 104 | Env* env_; 105 | 106 | int total_threads_limit_; 107 | std::atomic_uint queue_len_; // Queue length. Used for stats reporting 108 | bool exit_all_threads_; 109 | bool wait_for_jobs_to_complete_; 110 | 111 | // Entry per Schedule()/Submit() call 112 | struct BGItem { 113 | void* tag = nullptr; 114 | std::function function; 115 | std::function unschedFunction; 116 | }; 117 | 118 | using BGQueue = std::deque; 119 | BGQueue queue_; 120 | 121 | std::mutex mu_; 122 | std::condition_variable bgsignal_; 123 | std::vector bgthreads_; 124 | }; 125 | 126 | inline 127 | ThreadPoolImpl::Impl::Impl() 128 | : 129 | low_io_priority_(false), 130 | low_cpu_priority_(false), 131 | priority_(Env::LOW), 132 | env_(nullptr), 133 | total_threads_limit_(0), 134 | queue_len_(), 135 | exit_all_threads_(false), 136 | wait_for_jobs_to_complete_(false), 137 | queue_(), 138 | mu_(), 139 | bgsignal_(), 140 | bgthreads_() { 141 | } 142 | 143 | inline 144 | ThreadPoolImpl::Impl::~Impl() { assert(bgthreads_.size() == 0U); } 145 | 146 | void ThreadPoolImpl::Impl::JoinThreads(bool wait_for_jobs_to_complete) { 147 | 148 | std::unique_lock lock(mu_); 149 | assert(!exit_all_threads_); 150 | 151 | wait_for_jobs_to_complete_ = wait_for_jobs_to_complete; 152 | exit_all_threads_ = true; 153 | // prevent threads from being recreated right after they're joined, in case 154 | // the user is concurrently submitting jobs. 155 | total_threads_limit_ = 0; 156 | 157 | lock.unlock(); 158 | 159 | bgsignal_.notify_all(); 160 | 161 | for (auto& th : bgthreads_) { 162 | th.join(); 163 | } 164 | 165 | bgthreads_.clear(); 166 | 167 | exit_all_threads_ = false; 168 | wait_for_jobs_to_complete_ = false; 169 | } 170 | 171 | inline 172 | void ThreadPoolImpl::Impl::LowerIOPriority() { 173 | std::lock_guard lock(mu_); 174 | low_io_priority_ = true; 175 | } 176 | 177 | inline 178 | void ThreadPoolImpl::Impl::LowerCPUPriority() { 179 | std::lock_guard lock(mu_); 180 | low_cpu_priority_ = true; 181 | } 182 | 183 | void ThreadPoolImpl::Impl::BGThread(size_t thread_id) { 184 | bool low_io_priority = false; 185 | bool low_cpu_priority = false; 186 | 187 | while (true) { 188 | // Wait until there is an item that is ready to run 189 | std::unique_lock lock(mu_); 190 | // Stop waiting if the thread needs to do work or needs to terminate. 191 | while (!exit_all_threads_ && !IsLastExcessiveThread(thread_id) && 192 | (queue_.empty() || IsExcessiveThread(thread_id))) { 193 | bgsignal_.wait(lock); 194 | } 195 | 196 | if (exit_all_threads_) { // mechanism to let BG threads exit safely 197 | 198 | if (!wait_for_jobs_to_complete_ || 199 | queue_.empty()) { 200 | break; 201 | } 202 | } 203 | 204 | if (IsLastExcessiveThread(thread_id)) { 205 | // Current thread is the last generated one and is excessive. 206 | // We always terminate excessive thread in the reverse order of 207 | // generation time. 208 | auto& terminating_thread = bgthreads_.back(); 209 | terminating_thread.detach(); 210 | bgthreads_.pop_back(); 211 | 212 | if (HasExcessiveThread()) { 213 | // There is still at least more excessive thread to terminate. 214 | WakeUpAllThreads(); 215 | } 216 | break; 217 | } 218 | 219 | auto func = std::move(queue_.front().function); 220 | queue_.pop_front(); 221 | 222 | queue_len_.store(static_cast(queue_.size()), 223 | std::memory_order_relaxed); 224 | 225 | bool decrease_io_priority = (low_io_priority != low_io_priority_); 226 | bool decrease_cpu_priority = (low_cpu_priority != low_cpu_priority_); 227 | lock.unlock(); 228 | 229 | #ifdef OS_LINUX 230 | if (decrease_cpu_priority) { 231 | setpriority( 232 | PRIO_PROCESS, 233 | // Current thread. 234 | 0, 235 | // Lowest priority possible. 236 | 19); 237 | low_cpu_priority = true; 238 | } 239 | 240 | if (decrease_io_priority) { 241 | #define IOPRIO_CLASS_SHIFT (13) 242 | #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) 243 | // Put schedule into IOPRIO_CLASS_IDLE class (lowest) 244 | // These system calls only have an effect when used in conjunction 245 | // with an I/O scheduler that supports I/O priorities. As at 246 | // kernel 2.6.17 the only such scheduler is the Completely 247 | // Fair Queuing (CFQ) I/O scheduler. 248 | // To change scheduler: 249 | // echo cfq > /sys/block//queue/schedule 250 | // Tunables to consider: 251 | // /sys/block//queue/slice_idle 252 | // /sys/block//queue/slice_sync 253 | syscall(SYS_ioprio_set, 1, // IOPRIO_WHO_PROCESS 254 | 0, // current thread 255 | IOPRIO_PRIO_VALUE(3, 0)); 256 | low_io_priority = true; 257 | } 258 | #else 259 | (void)decrease_io_priority; // avoid 'unused variable' error 260 | (void)decrease_cpu_priority; 261 | #endif 262 | func(); 263 | } 264 | } 265 | 266 | // Helper struct for passing arguments when creating threads. 267 | struct BGThreadMetadata { 268 | ThreadPoolImpl::Impl* thread_pool_; 269 | size_t thread_id_; // Thread count in the thread. 270 | BGThreadMetadata(ThreadPoolImpl::Impl* thread_pool, size_t thread_id) 271 | : thread_pool_(thread_pool), thread_id_(thread_id) {} 272 | }; 273 | 274 | void* ThreadPoolImpl::Impl::BGThreadWrapper(void* arg) { 275 | BGThreadMetadata* meta = reinterpret_cast(arg); 276 | size_t thread_id = meta->thread_id_; 277 | ThreadPoolImpl::Impl* tp = meta->thread_pool_; 278 | 279 | delete meta; 280 | tp->BGThread(thread_id); 281 | return nullptr; 282 | } 283 | 284 | void ThreadPoolImpl::Impl::SetBackgroundThreadsInternal(int num, 285 | bool allow_reduce) { 286 | std::unique_lock lock(mu_); 287 | if (exit_all_threads_) { 288 | lock.unlock(); 289 | return; 290 | } 291 | if (num > total_threads_limit_ || 292 | (num < total_threads_limit_ && allow_reduce)) { 293 | total_threads_limit_ = std::max(0, num); 294 | WakeUpAllThreads(); 295 | StartBGThreads(); 296 | } 297 | } 298 | 299 | int ThreadPoolImpl::Impl::GetBackgroundThreads() { 300 | std::unique_lock lock(mu_); 301 | return total_threads_limit_; 302 | } 303 | 304 | void ThreadPoolImpl::Impl::StartBGThreads() { 305 | // Start background thread if necessary 306 | while ((int)bgthreads_.size() < total_threads_limit_) { 307 | 308 | std::thread p_t(&BGThreadWrapper, 309 | new BGThreadMetadata(this, bgthreads_.size())); 310 | 311 | bgthreads_.push_back(std::move(p_t)); 312 | } 313 | } 314 | 315 | void ThreadPoolImpl::Impl::Submit(std::function&& schedule, 316 | std::function&& unschedule, void* tag) { 317 | 318 | std::lock_guard lock(mu_); 319 | 320 | if (exit_all_threads_) { 321 | return; 322 | } 323 | 324 | StartBGThreads(); 325 | 326 | // Add to priority queue 327 | queue_.push_back(BGItem()); 328 | 329 | auto& item = queue_.back(); 330 | item.tag = tag; 331 | item.function = std::move(schedule); 332 | item.unschedFunction = std::move(unschedule); 333 | 334 | queue_len_.store(static_cast(queue_.size()), 335 | std::memory_order_relaxed); 336 | 337 | if (!HasExcessiveThread()) { 338 | // Wake up at least one waiting thread. 339 | bgsignal_.notify_one(); 340 | } else { 341 | // Need to wake up all threads to make sure the one woken 342 | // up is not the one to terminate. 343 | WakeUpAllThreads(); 344 | } 345 | } 346 | 347 | int ThreadPoolImpl::Impl::UnSchedule(void* arg) { 348 | int count = 0; 349 | 350 | std::vector> candidates; 351 | { 352 | std::lock_guard lock(mu_); 353 | 354 | // Remove from priority queue 355 | BGQueue::iterator it = queue_.begin(); 356 | while (it != queue_.end()) { 357 | if (arg == (*it).tag) { 358 | if (it->unschedFunction) { 359 | candidates.push_back(std::move(it->unschedFunction)); 360 | } 361 | it = queue_.erase(it); 362 | count++; 363 | } else { 364 | ++it; 365 | } 366 | } 367 | queue_len_.store(static_cast(queue_.size()), 368 | std::memory_order_relaxed); 369 | } 370 | 371 | 372 | // Run unschedule functions outside the mutex 373 | for (auto& f : candidates) { 374 | f(); 375 | } 376 | 377 | return count; 378 | } 379 | 380 | ThreadPoolImpl::ThreadPoolImpl() : 381 | impl_(new Impl()) { 382 | } 383 | 384 | 385 | ThreadPoolImpl::~ThreadPoolImpl() { 386 | } 387 | 388 | void ThreadPoolImpl::JoinAllThreads() { 389 | impl_->JoinThreads(false); 390 | } 391 | 392 | void ThreadPoolImpl::SetBackgroundThreads(int num) { 393 | impl_->SetBackgroundThreadsInternal(num, true); 394 | } 395 | 396 | int ThreadPoolImpl::GetBackgroundThreads() { 397 | return impl_->GetBackgroundThreads(); 398 | } 399 | 400 | unsigned int ThreadPoolImpl::GetQueueLen() const { 401 | return impl_->GetQueueLen(); 402 | } 403 | 404 | void ThreadPoolImpl::WaitForJobsAndJoinAllThreads() { 405 | impl_->JoinThreads(true); 406 | } 407 | 408 | void ThreadPoolImpl::LowerIOPriority() { 409 | impl_->LowerIOPriority(); 410 | } 411 | 412 | void ThreadPoolImpl::LowerCPUPriority() { 413 | impl_->LowerCPUPriority(); 414 | } 415 | 416 | void ThreadPoolImpl::IncBackgroundThreadsIfNeeded(int num) { 417 | impl_->SetBackgroundThreadsInternal(num, false); 418 | } 419 | 420 | void ThreadPoolImpl::SubmitJob(const std::function& job) { 421 | auto copy(job); 422 | impl_->Submit(std::move(copy), std::function(), nullptr); 423 | } 424 | 425 | 426 | void ThreadPoolImpl::SubmitJob(std::function&& job) { 427 | impl_->Submit(std::move(job), std::function(), nullptr); 428 | } 429 | 430 | void ThreadPoolImpl::Schedule(void(*function)(void* arg1), void* arg, 431 | void* tag, void(*unschedFunction)(void* arg)) { 432 | if (unschedFunction == nullptr) { 433 | impl_->Submit(std::bind(function, arg), std::function(), tag); 434 | } else { 435 | impl_->Submit(std::bind(function, arg), std::bind(unschedFunction, arg), 436 | tag); 437 | } 438 | } 439 | 440 | int ThreadPoolImpl::UnSchedule(void* arg) { 441 | return impl_->UnSchedule(arg); 442 | } 443 | 444 | void ThreadPoolImpl::SetHostEnv(Env* env) { impl_->SetHostEnv(env); } 445 | 446 | Env* ThreadPoolImpl::GetHostEnv() const { return impl_->GetHostEnv(); } 447 | 448 | // Return the thread priority. 449 | // This would allow its member-thread to know its priority. 450 | Env::Priority ThreadPoolImpl::GetThreadPriority() const { 451 | return impl_->GetThreadPriority(); 452 | } 453 | 454 | // Set the thread priority. 455 | void ThreadPoolImpl::SetThreadPriority(Env::Priority priority) { 456 | impl_->SetThreadPriority(priority); 457 | } 458 | 459 | ThreadPool* NewThreadPool(int num_threads) { 460 | ThreadPoolImpl* thread_pool = new ThreadPoolImpl(); 461 | thread_pool->SetBackgroundThreads(num_threads); 462 | return thread_pool; 463 | } 464 | 465 | --------------------------------------------------------------------------------