9 static const char rcsid[] __attribute__((used)) =
"$Id: ThreadPool.cpp 1653 2016-02-28 19:54:59Z sella $";
11 #include "ThreadPool.h"
12 #include "CommonMacro.h"
24 using namespace sella::util;
25 using namespace std::chrono;
27 ThreadPool::ThreadPool(
size_t workers)
throw (ThreadPoolException) :
38 spares(DefaultSpareWorkers)
43 TRACE(PACKAGE,
"ThreadPool starting up with %zd workers", this->maximum);
46 managerTID = std::thread(&ThreadPool::manager,
this);
47 tids.insert(managerTID.native_handle());
48 }
catch (std::system_error &e) {
49 THROW(ThreadPoolException,
"Failed to start manager thread: %s", e.what());
53 ThreadPool::ThreadPool(
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ThreadPoolException) :
62 maximum(maximumWorkers),
63 minimum(minimumWorkers),
69 TRACE(PACKAGE,
"ThreadPool starting up with %zd/%zd workers, %zd spares", this->minimum, this->maximum, this->spares);
72 managerTID = std::thread(&ThreadPool::manager,
this);
73 tids.insert(managerTID.native_handle());
74 }
catch (std::system_error &e) {
75 THROW(ThreadPoolException,
"Failed to start manager thread: %s", e.what());
79 ThreadPool::ThreadPool(Log::shared log,
size_t workers)
throw (ThreadPoolException) :
90 spares(DefaultSpareWorkers)
95 TRACE(PACKAGE,
"ThreadPool starting up with %zd workers", this->maximum);
98 managerTID = std::thread(&ThreadPool::manager,
this);
99 tids.insert(managerTID.native_handle());
100 }
catch (std::system_error &e) {
101 THROW(ThreadPoolException,
"Failed to start manager thread: %s", e.what());
105 ThreadPool::ThreadPool(Log::shared log,
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ThreadPoolException) :
114 maximum(maximumWorkers),
115 minimum(minimumWorkers),
121 TRACE(PACKAGE,
"ThreadPool starting up with %zd/%zd workers, %zd spares", this->minimum, this->maximum, this->spares);
124 managerTID = std::thread(&ThreadPool::manager,
this);
125 tids.insert(managerTID.native_handle());
126 }
catch (std::system_error &e) {
127 THROW(ThreadPoolException,
"Failed to start manager thread: %s", e.what());
131 ThreadPool::~ThreadPool() {
132 TRACE(PACKAGE,
"ThreadPool shutting down with %zd queued, %zd workers, %zd active", heap.size(), (size_t) threads, (
size_t) actives);
136 managerCV.notify_all();
137 managerMutex.unlock();
139 if (managerTID.joinable()) {
143 std::lock_guard<std::mutex> lg(emptyMutex);
144 emptyCV.notify_all();
147 void ThreadPool::schedule(Task::shared &task,
int priority) {
148 assert(task.get() != NULL);
150 if (priority) task->setPriority(priority);
152 TRACE(PACKAGE
"-detail",
"Scheduling task %p to run immedately with priority %d", task.get(), task->getPriority());
158 std::lock_guard<std::mutex> lg(managerMutex);
159 managerCV.notify_all();
162 void ThreadPool::schedule(Task::shared &task,
const std::chrono::system_clock::duration &delay,
int priority) {
163 assert(task.get() != NULL);
165 task->setDelay(delay);
166 if (priority) task->setPriority(priority);
168 TRACE(PACKAGE
"-detail",
"Scheduling task %p to run in %lld.%lld sec with priority %d", task.get(), task->getDelay().tv_sec, task->getDelay().tv_usec, task->getPriority());
174 std::lock_guard<std::mutex> lg(managerMutex);
175 managerCV.notify_all();
178 void ThreadPool::schedule(Task::shared &task,
const std::chrono::system_clock::time_point &time,
int priority) {
179 assert(task.get() != NULL);
181 task->setTimer(time);
182 if (priority) task->setPriority(priority);
184 TRACE(PACKAGE
"-detail",
"Scheduling task %p to run at %lld.%lld sec with priority %d", task.get(), task->getTimer().tv_sec, task->getTimer().tv_usec, task->getPriority());
190 std::lock_guard<std::mutex> lg(managerMutex);
191 managerCV.notify_all();
194 void ThreadPool::schedule(Task::shared &task, time_t sec, suseconds_t usec,
int priority) {
195 assert(task.get() != NULL);
197 task->setDelay(sec, usec);
198 if (priority) task->setPriority(priority);
200 TRACE(PACKAGE
"-detail",
"Scheduling task %p to run in %lld.%lld sec with priority %d", task.get(), sec, usec, task->getPriority());
206 std::lock_guard<std::mutex> lg(managerMutex);
207 managerCV.notify_all();
210 void ThreadPool::schedule(Task::shared &task, timeval &tv,
int priority) {
211 assert(task.get() != NULL);
214 if (priority) task->setPriority(priority);
216 TRACE(PACKAGE
"-detail",
"Scheduling task %p to run at %lld.%lld sec with priority %d", task.get(), Time::tvtosec(tv), Time::tvtousec(tv), task->getPriority());
222 std::lock_guard<std::mutex> lg(managerMutex);
223 managerCV.notify_all();
226 bool ThreadPool::reschedule(Task::shared &task,
const std::chrono::system_clock::duration &delay,
int priority,
bool strict) {
227 assert(task.get() != NULL);
229 task->setDelay(delay);
230 if (priority) task->setPriority(priority);
232 TRACE(PACKAGE
"-detail",
"Rescheduling task %p to run in %lld.%lld sec with priority %d", task.get(), task->getDelay().tv_sec, task->getDelay().tv_usec, task->getPriority());
235 bool res = repush(task, delay, priority, strict);
238 std::lock_guard<std::mutex> lg(managerMutex);
239 managerCV.notify_all();
244 bool ThreadPool::reschedule(Task::shared &task,
const std::chrono::system_clock::time_point &time,
int priority,
bool strict) {
245 assert(task.get() != NULL);
247 task->setTimer(time);
248 if (priority) task->setPriority(priority);
250 TRACE(PACKAGE
"-detail",
"Rescheduling task %p to run at %lld.%lld sec with priority %d", task.get(), task->getTimer().tv_sec, task->getTimer().tv_usec, task->getPriority());
253 bool res = repush(task, time, priority, strict);
256 std::lock_guard<std::mutex> lg(managerMutex);
257 managerCV.notify_all();
262 bool ThreadPool::reschedule(Task::shared &task, time_t sec, suseconds_t usec,
int priority,
bool strict) {
263 assert(task.get() != NULL);
265 if (priority) task->setPriority(priority);
267 TRACE(PACKAGE
"-detail",
"Rescheduling task %p to run in %lld.%lld sec with priority %d", task.get(), sec, usec, task->getPriority());
270 bool res = repush(task, sec, usec, priority, strict);
273 std::lock_guard<std::mutex> lg(managerMutex);
274 managerCV.notify_all();
279 bool ThreadPool::reschedule(Task::shared &task, timeval &tv,
int priority,
bool strict) {
280 assert(task.get() != NULL);
282 if (priority) task->setPriority(priority);
284 TRACE(PACKAGE
"-detail",
"Rescheduling task %p to run at %lld.%lld sec with priority %d", task.get(), tv.tv_sec, tv.tv_usec, task->getPriority());
287 bool res = repush(task, tv, priority, strict);
290 std::lock_guard<std::mutex> lg(managerMutex);
291 managerCV.notify_all();
296 bool ThreadPool::unschedule(Task::shared &task,
bool firstonly) {
297 TRACE(PACKAGE
"-detail",
"Unscheduling task %p%s", task.get(), (firstonly) ?
" (firstonly)" :
"");
300 bool res = erase(task, firstonly);
303 std::lock_guard<std::mutex> lg(managerMutex);
304 managerCV.notify_all();
309 void ThreadPool::pause(
bool on) {
310 std::lock_guard<std::mutex> lg(managerMutex);
312 if ((paused = on) ==
false) {
313 TRACE(PACKAGE,
"Resuming %zd queued, %zd workers, %zd active", heap.size(), (size_t) threads, (
size_t) actives);
315 managerCV.notify_all();
317 TRACE(PACKAGE,
"Pausing %zd queued, %zd workers, %zd active", heap.size(), (size_t) threads, (
size_t) actives);
321 size_t ThreadPool::getCPUCount(
void) {
322 return (std::thread::hardware_concurrency() > 0) ? std::thread::hardware_concurrency() : sysconf(_SC_NPROCESSORS_ONLN);
325 int ThreadPool::getCPUSetCount(
void) {
326 return CPU_COUNT(&cpuset);
329 bool ThreadPool::addCPUAffinity(std::set<int> cpus) {
330 size_t count = getCPUCount();
336 for (
auto c = cpus.begin(); c != cpus.end(); ++c) {
337 if ((
size_t) *c < count) {
338 CPU_SET(*c, &cpuset);
342 if (CPU_COUNT(&cpuset) == 0) {
348 return setThreadsAffinity();
351 bool ThreadPool::addCPUAffinity(
int cpu) {
352 size_t count = getCPUCount();
358 if ((
size_t) cpu < count) {
359 CPU_SET(cpu, &cpuset);
362 if (CPU_COUNT(&cpuset) == 0) {
368 return setThreadsAffinity();
371 bool ThreadPool::clearCPUAffinity(
void) {
375 size_t cpus = getCPUCount();
377 for (
size_t i = 0; i < cpus; i++) {
381 return setThreadsAffinity();
384 bool ThreadPool::clearCPUAffinity(
int cpu) {
385 if (cpu < CPU_SETSIZE) {
386 CPU_CLR(cpu, &cpuset);
389 if (CPU_COUNT(&cpuset) == 0) {
393 return setThreadsAffinity();
396 bool ThreadPool::clearCPUAffinity(std::set<int> cpus) {
397 for (
auto c = cpus.begin(); c != cpus.end(); ++c) {
398 if (*c < CPU_SETSIZE) {
399 CPU_CLR(*c, &cpuset);
403 if (CPU_COUNT(&cpuset) == 0) {
407 return setThreadsAffinity();
410 size_t ThreadPool::size(
void)
const {
411 std::lock_guard<std::mutex> lg(heapMutex);
413 return TaskQueue::size() + actives;
416 size_t ThreadPool::workers(
void)
const {
420 size_t ThreadPool::active(
void)
const {
424 bool ThreadPool::empty(
void)
const {
425 return (size() == 0);
428 void ThreadPool::clear(
void) {
433 std::lock_guard<std::mutex> lg(managerMutex);
434 managerCV.notify_all();
437 Task::shared ThreadPool::next(
void) {
438 std::lock_guard<std::mutex> lg(heapMutex);
440 if (!TaskQueue::empty()) {
441 Task::shared task = top();
443 if (task && Task::compareTimer(task)) {
450 return Task::shared();
453 microseconds ThreadPool::duration(
void)
const {
454 if (actives == maximum || UNLIKELY(paused && !shutdown)) {
458 std::lock_guard<std::mutex> lg(heapMutex);
460 if (!TaskQueue::empty()) {
461 Task::shared task = top();
462 timeval delay = task->getDelay();
464 if (delay.tv_usec < 1 && delay.tv_sec < 1) {
466 return milliseconds(10);
468 return milliseconds(2);
472 return seconds(delay.tv_sec) + microseconds(delay.tv_usec);
473 }
else if (UNLIKELY(shutdown)) {
474 return milliseconds(1);
480 microseconds ThreadPool::max(
void)
const {
481 return hours(24 * 365 * 100);
484 bool ThreadPool::ready(
void)
const {
485 if (TaskQueue::empty()) {
489 std::lock_guard<std::mutex> lg(heapMutex);
491 return Task::compareTimer(top());
494 bool ThreadPool::wait(
void) {
495 std::unique_lock<std::mutex> lock(emptyMutex);
499 managerCV.notify_all();
500 managerMutex.unlock();
506 managerCV.notify_all();
507 managerMutex.unlock();
514 bool ThreadPool::wait(
const std::chrono::system_clock::duration &delay) {
515 std::unique_lock<std::mutex> lock(emptyMutex);
519 managerCV.notify_all();
520 managerMutex.unlock();
526 managerCV.notify_all();
527 managerMutex.unlock();
529 emptyCV.wait_for(lock, delay);
534 bool ThreadPool::wait(
const std::chrono::system_clock::time_point &time) {
535 std::unique_lock<std::mutex> lock(emptyMutex);
539 managerCV.notify_all();
540 managerMutex.unlock();
546 managerCV.notify_all();
547 managerMutex.unlock();
549 emptyCV.wait_until(lock, time);
554 void ThreadPool::reserve(
size_t capacity) {
555 std::lock_guard<std::mutex> lg(heapMutex);
556 TaskQueue::reserve(capacity);
559 void ThreadPool::print(
void)
const {
560 std::lock_guard<std::mutex> lg(heapMutex);
563 timeval now, timer, delay;
565 if (libutilxx__log == NULL) {
569 gettimeofday(&now, NULL);
570 INFO(
"[ThreadPool Heap] - now %10lu.%06lu, %zd, queued, %zd workers, %zd active, %zd overage", now.tv_sec, now.tv_usec, heap.size(), (size_t) threads, (
size_t) actives, (size_t) overage);
573 INFO(
" heap => empty");
578 for (
auto it = heap.begin(); it != heap.end(); ++it) {
579 timer = (*it)->getTimer();
580 if (timerisset(&timer)) {
582 delay.tv_sec = timer.tv_sec - now.tv_sec;
583 delay.tv_usec = timer.tv_usec - now.tv_usec;
584 if (delay.tv_sec > 0 && delay.tv_usec < 0) {
586 delay.tv_usec += 1000000;
589 INFO(
" heap => task(%10p) obj: %10p pri: %3d timer: %10lu.%06lu delay: %4d.%d", (*it).get(), (*it)->getObject(), (*it)->getPriority(), timer.tv_sec, timer.tv_usec, (signed) delay.tv_sec, (
signed) delay.tv_usec);
591 INFO(
" heap => task(%10p) obj: %10p pri: %3d timer: %10lu.%06lu delay: -inf", (*it).get(), (*it)->getObject(), (*it)->getPriority(), timer.tv_sec, timer.tv_usec);
596 ThreadPool::shared ThreadPool::shared_ptr(
size_t maximumWorkers)
throw (ThreadPoolException) {
597 return std::make_shared<ThreadPool>(maximumWorkers);
600 ThreadPool::shared ThreadPool::shared_ptr(
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ThreadPoolException) {
601 return std::make_shared<ThreadPool>(maximumWorkers, minimumWorkers, spareWorkers);
604 ThreadPool::shared ThreadPool::shared_ptr(Log::shared log,
size_t maximumWorkers)
throw (ThreadPoolException) {
605 return std::make_shared<ThreadPool>(log, maximumWorkers);
608 ThreadPool::shared ThreadPool::shared_ptr(Log::shared log,
size_t maximumWorkers,
size_t minimumWorkers,
size_t spareWorkers)
throw (ThreadPoolException) {
609 return std::make_shared<ThreadPool>(log, maximumWorkers, minimumWorkers, spareWorkers);
612 void ThreadPool::manager(
void) {
615 for (
size_t i = 0; !shutdown && i < minimum; i++) {
619 TRACE(PACKAGE
"-detail",
"Manager started up with %zd threads", (
size_t) threads);
621 while (LIKELY(!shutdown)) {
622 if (minimum == maximum) {
623 while (LIKELY(!shutdown) && threads < minimum) {
627 while (LIKELY(!shutdown) && threads < maximum && threads < actives + spares) {
631 if (ready() || (++cycles % maximum) != 0) {
634 if (threads > minimum && threads > actives + spares) {
635 overage = threads - actives + spares;
636 }
else if (actives == 0) {
637 overage = threads - minimum;
640 TRACE(PACKAGE
"-detail",
"Manager set overage to %zd", (
size_t) overage);
642 for (
size_t i = 0; LIKELY(!shutdown) && i < overage && i < maximum; i++) {
643 TRACE(PACKAGE
"-detail",
"Manager waking worker for overage");
645 std::lock_guard<std::mutex> lg(workerMutex);
646 workerCV.notify_one();
651 if (LIKELY(!shutdown)) {
652 TRACE(PACKAGE,
"Manager waiting for tasks - %zd queued, %zd active, %zd/%zd workers", heap.size(), (size_t) actives, (
size_t) threads, maximum);
654 std::unique_lock<std::mutex> lock(managerMutex);
658 emptyCV.notify_all();
662 microseconds delay = duration();
664 TRACE(PACKAGE
"-detail",
"delay: %lld, active: %zd, threads: %zd, overage: %zd, shutdown: %d", microseconds(delay).count(), (
size_t) actives, (
size_t) threads, (
size_t) overage, shutdown);
666 managerCV.wait_for(lock, delay);
667 }
while (UNLIKELY(paused && !shutdown));
670 if (LIKELY(!shutdown)) {
671 size_t max = maximum - actives;
673 TRACE(PACKAGE,
"Manager waking %zd worker(s) - %zd queued, %zd active, %zd/%zd workers", max, heap.size(), (size_t) actives, (
size_t) threads, maximum);
675 std::lock_guard<std::mutex> lg(workerMutex);
676 for (
size_t i = 0; ready() && i < max; i++) {
677 workerCV.notify_one();
682 TRACE(PACKAGE
"-detail",
"Manager waiting on remaining %zd workers to complete", (
size_t) threads);
685 overage = (size_t) threads;
686 workerCV.notify_all();
688 while (threads > 0) {
689 std::unique_lock<std::mutex> lock(managerMutex);
690 managerCV.wait_for(lock, seconds(1));
692 workerCV.notify_all();
695 std::lock_guard<std::mutex> lg(emptyMutex);
696 emptyCV.notify_all();
698 TRACE(PACKAGE,
"Manager shutdown");
701 void ThreadPool::worker(
void) {
703 int tid = syscall(SYS_gettid);
705 TRACE(PACKAGE,
"Worker(%d) started", tid);
710 while (LIKELY(!shutdown)) {
712 TRACE(PACKAGE
"-detail",
"Worker(%d) waiting for next task", tid);
714 std::unique_lock<std::mutex> lock(workerMutex);
717 managerCV.notify_all();
718 managerMutex.unlock();
725 if (LIKELY(!shutdown)) {
727 std::lock_guard<std::mutex> lg(overageMutex);
730 TRACE(PACKAGE
"-detail",
"Worker(%d) yielding up the ghost (spare worker)", tid);
743 TRACE(PACKAGE
"-detail",
"Worker(%d) acquired task %p with priority %d", tid, task.get(), task->getPriority());
746 workerMutex.unlock();
749 TRACE(PACKAGE,
"Worker(%d) executing task %p with priority %d", tid, task.get(), task->getPriority());
757 TRACE(PACKAGE,
"Worker(%d) ended", tid);
760 tids.erase(pthread_self());
766 std::lock_guard<std::mutex> lg(managerMutex);
767 managerCV.notify_all();
770 void ThreadPool::normalize(
void) {
772 maximum = getCPUCount();
776 minimum = getCPUCount();
780 spares = DefaultSpareWorkers;
783 if (minimum > maximum) {
786 }
else if (maximum == minimum) {
791 void ThreadPool::createWorker(
void) {
792 TRACE(PACKAGE
"-detail",
"Manager starting up worker %zd/%zd with affinity to %d CPUs", (
size_t) threads + 1, maximum, getCPUSetCount());
795 std::thread t(&ThreadPool::worker,
this);
796 setThreadAffinity(t.native_handle());
799 tids.insert(t.native_handle());
805 }
catch (std::exception &e) {
806 TRACE(PACKAGE,
"Failed to create worker thread: %s", e.what());
810 bool ThreadPool::setThreadsAffinity() {
811 std::lock_guard<std::mutex> lg(tidsMutex);
813 TRACE(PACKAGE
"-detail",
"Setting %zd threads affinity to %d CPUs", tids.size(), getCPUSetCount());
815 for (
auto t = tids.begin(); t != tids.end(); ++t) {
816 if (!setThreadAffinity(*t)) {
824 bool ThreadPool::setThreadAffinity(pthread_t tid) {
827 if ((error = pthread_setaffinity_np(tid,
sizeof(cpu_set_t), &cpuset)) != 0) {
828 char buf[1024] =
"message unavailable";
829 char *e = strerror_r(error, buf,
sizeof(buf));
832 TRACE(PACKAGE,
"pthread_setaffinity_np: %d:%s", error, buf);