Shaka Packager SDK
Loading...
Searching...
No Matches
thread_pool.cc
1// Copyright 2022 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/file/thread_pool.h>
8
9#include <functional>
10#include <thread>
11#include <utility>
12
13#include <absl/log/check.h>
14#include <absl/log/log.h>
15#include <absl/synchronization/mutex.h>
16#include <absl/time/time.h>
17
18namespace shaka {
19
20namespace {
21
22const absl::Duration kMaxThreadIdleTime = absl::Minutes(10);
23
24} // namespace
25
26// static
27ThreadPool ThreadPool::instance;
28
29ThreadPool::ThreadPool() : num_idle_threads_(0), terminated_(false) {}
30
31ThreadPool::~ThreadPool() {
32 Terminate();
33}
34
35void ThreadPool::PostTask(const std::function<void()>& task) {
36 absl::MutexLock lock(mutex_);
37
38 DCHECK(!terminated_) << "Should not call PostTask after Terminate!";
39
40 if (terminated_) {
41 return;
42 }
43
44 // An empty task is used internally to signal the thread to terminate. This
45 // should never be sent on input.
46 if (!task) {
47 DLOG(ERROR) << "Should not post an empty task!";
48 return;
49 }
50
51 tasks_.push(std::move(task));
52
53 if (num_idle_threads_ >= tasks_.size()) {
54 // We have enough threads available.
55 tasks_available_.SignalAll();
56 } else {
57 // We need to start an additional thread.
58 std::thread thread(std::bind(&ThreadPool::ThreadMain, this));
59 thread.detach();
60 }
61}
62
63void ThreadPool::Terminate() {
64 {
65 absl::MutexLock lock(mutex_);
66 terminated_ = true;
67 while (!tasks_.empty()) {
68 tasks_.pop();
69 }
70 }
71 tasks_available_.SignalAll();
72}
73
74ThreadPool::Task ThreadPool::WaitForTask() {
75 absl::MutexLock lock(mutex_);
76 if (terminated_) {
77 // The pool is terminated. Terminate this thread.
78 return Task();
79 }
80
81 if (tasks_.empty()) {
82 num_idle_threads_++;
83 // Wait for a task, up to the maximum idle time.
84 tasks_available_.WaitWithTimeout(&mutex_, kMaxThreadIdleTime);
85 num_idle_threads_--;
86
87 if (tasks_.empty()) {
88 // No work before the timeout. Terminate this thread.
89 return Task();
90 }
91 }
92
93 // Get the next task from the queue.
94 Task task = tasks_.front();
95 tasks_.pop();
96 return task;
97}
98
99void ThreadPool::ThreadMain() {
100 while (true) {
101 auto task = WaitForTask();
102 if (!task) {
103 // An empty task signals the thread to terminate.
104 return;
105 }
106
107 // Run the task, then loop to wait for another.
108 task();
109 }
110}
111
112} // namespace shaka
All the methods that are virtual are virtual for mocking.