blob: 31fcef18bbf06f6cfb19c769bdf11c511e72d50b [file] [log] [blame]
[email protected]5846da02009-03-06 20:35:451// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/worker_pool.h"
[email protected]250103f2009-03-24 23:41:536#include "base/worker_pool_linux.h"
[email protected]5846da02009-03-06 20:35:457
[email protected]250103f2009-03-24 23:41:538#include "base/lazy_instance.h"
[email protected]56d01f62009-03-12 22:41:549#include "base/logging.h"
[email protected]250103f2009-03-24 23:41:5310#include "base/platform_thread.h"
11#include "base/ref_counted.h"
12#include "base/string_util.h"
[email protected]5846da02009-03-06 20:35:4513#include "base/task.h"
14
15namespace {
16
[email protected]250103f2009-03-24 23:41:5317const int kIdleSecondsBeforeExit = 10 * 60;
[email protected]82fbbe62009-05-28 08:45:1918// A stack size of 64 KB is too small for the CERT_PKIXVerifyCert
19// function of NSS because of NSS bug 439169.
20const int kWorkerThreadStackSize = 128 * 1024;
[email protected]250103f2009-03-24 23:41:5321
22class WorkerPoolImpl {
23 public:
24 WorkerPoolImpl();
25 ~WorkerPoolImpl();
26
27 void PostTask(const tracked_objects::Location& from_here, Task* task,
28 bool task_is_slow);
29
30 private:
31 scoped_refptr<base::LinuxDynamicThreadPool> pool_;
32};
33
34WorkerPoolImpl::WorkerPoolImpl()
35 : pool_(new base::LinuxDynamicThreadPool(
36 "WorkerPool", kIdleSecondsBeforeExit)) {}
37
38WorkerPoolImpl::~WorkerPoolImpl() {
39 pool_->Terminate();
40}
41
42void WorkerPoolImpl::PostTask(const tracked_objects::Location& from_here,
43 Task* task, bool task_is_slow) {
44 task->SetBirthPlace(from_here);
45 pool_->PostTask(task);
46}
47
48base::LazyInstance<WorkerPoolImpl> g_lazy_worker_pool(base::LINKER_INITIALIZED);
49
50class WorkerThread : public PlatformThread::Delegate {
51 public:
52 WorkerThread(const std::string& name_prefix, int idle_seconds_before_exit,
53 base::LinuxDynamicThreadPool* pool)
54 : name_prefix_(name_prefix),
55 idle_seconds_before_exit_(idle_seconds_before_exit),
56 pool_(pool) {}
57
58 virtual void ThreadMain();
59
60 private:
61 const std::string name_prefix_;
62 const int idle_seconds_before_exit_;
63 scoped_refptr<base::LinuxDynamicThreadPool> pool_;
64
65 DISALLOW_COPY_AND_ASSIGN(WorkerThread);
66};
67
68void WorkerThread::ThreadMain() {
69 const std::string name =
70 StringPrintf("%s/%d", name_prefix_.c_str(),
71 IntToString(PlatformThread::CurrentId()).c_str());
72 PlatformThread::SetName(name.c_str());
73
74 for (;;) {
75 Task* task = pool_->WaitForTask();
76 if (!task)
77 break;
78 task->Run();
79 delete task;
80 }
81
82 // The WorkerThread is non-joinable, so it deletes itself.
83 delete this;
[email protected]5846da02009-03-06 20:35:4584}
85
86} // namespace
87
88bool WorkerPool::PostTask(const tracked_objects::Location& from_here,
89 Task* task, bool task_is_slow) {
[email protected]250103f2009-03-24 23:41:5390 g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow);
[email protected]5846da02009-03-06 20:35:4591 return true;
92}
[email protected]250103f2009-03-24 23:41:5393
94namespace base {
95
96LinuxDynamicThreadPool::LinuxDynamicThreadPool(
97 const std::string& name_prefix,
98 int idle_seconds_before_exit)
99 : name_prefix_(name_prefix),
100 idle_seconds_before_exit_(idle_seconds_before_exit),
101 tasks_available_cv_(&lock_),
102 num_idle_threads_(0),
103 terminated_(false),
104 num_idle_threads_cv_(NULL) {}
105
106LinuxDynamicThreadPool::~LinuxDynamicThreadPool() {
107 while (!tasks_.empty()) {
108 Task* task = tasks_.front();
109 tasks_.pop();
110 delete task;
111 }
112}
113
114void LinuxDynamicThreadPool::Terminate() {
115 {
116 AutoLock locked(lock_);
117 DCHECK(!terminated_) << "Thread pool is already terminated.";
118 terminated_ = true;
119 }
120 tasks_available_cv_.Broadcast();
121}
122
123void LinuxDynamicThreadPool::PostTask(Task* task) {
124 AutoLock locked(lock_);
125 DCHECK(!terminated_) <<
126 "This thread pool is already terminated. Do not post new tasks.";
127
128 tasks_.push(task);
129
130 // We have enough worker threads.
131 if (static_cast<size_t>(num_idle_threads_) >= tasks_.size()) {
132 tasks_available_cv_.Signal();
133 } else {
134 // The new PlatformThread will take ownership of the WorkerThread object,
135 // which will delete itself on exit.
136 WorkerThread* worker =
137 new WorkerThread(name_prefix_, idle_seconds_before_exit_, this);
138 PlatformThread::CreateNonJoinable(kWorkerThreadStackSize, worker);
139 }
140}
141
142Task* LinuxDynamicThreadPool::WaitForTask() {
143 AutoLock locked(lock_);
144
145 if (terminated_)
146 return NULL;
147
148 if (tasks_.empty()) { // No work available, wait for work.
149 num_idle_threads_++;
150 if (num_idle_threads_cv_.get())
151 num_idle_threads_cv_->Signal();
152 tasks_available_cv_.TimedWait(
153 TimeDelta::FromSeconds(kIdleSecondsBeforeExit));
154 num_idle_threads_--;
155 if (num_idle_threads_cv_.get())
156 num_idle_threads_cv_->Signal();
157 if (tasks_.empty()) {
158 // We waited for work, but there's still no work. Return NULL to signal
159 // the thread to terminate.
160 return NULL;
161 }
162 }
163
164 Task* task = tasks_.front();
165 tasks_.pop();
166 return task;
167}
168
169} // namespace base