1b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat// Use of this source code is governed by a BSD-style license that can be
3b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat// found in the LICENSE file.
4b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
5b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/threading/worker_pool_posix.h"
6b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
7cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko#include <stddef.h>
8cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko
9b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/bind.h"
10b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/callback.h"
11b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/lazy_instance.h"
12b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/logging.h"
13cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko#include "base/macros.h"
14b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/memory/ref_counted.h"
15b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/strings/stringprintf.h"
16b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/threading/platform_thread.h"
17b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/threading/thread_local.h"
18b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/threading/worker_pool.h"
19b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/trace_event/trace_event.h"
20b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat#include "base/tracked_objects.h"
21b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
22b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratusing tracked_objects::TrackedTime;
23b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
24b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratnamespace base {
25b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
26b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratnamespace {
27b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
28b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratbase::LazyInstance<ThreadLocalBoolean>::Leaky
29b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    g_worker_pool_running_on_this_thread = LAZY_INSTANCE_INITIALIZER;
30b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
31b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratconst int kIdleSecondsBeforeExit = 10 * 60;
32b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
33b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratclass WorkerPoolImpl {
34b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat public:
35b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  WorkerPoolImpl();
36b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  ~WorkerPoolImpl();
37b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
38b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  void PostTask(const tracked_objects::Location& from_here,
39cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                const base::Closure& task,
40cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                bool task_is_slow);
41b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
42b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat private:
43b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  scoped_refptr<base::PosixDynamicThreadPool> pool_;
44b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat};
45b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
46b8cf94937c52feb53b55c39e3f82094d27de464cDaniel EratWorkerPoolImpl::WorkerPoolImpl()
47b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    : pool_(new base::PosixDynamicThreadPool("WorkerPool",
48cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                                             kIdleSecondsBeforeExit)) {}
49b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
50b8cf94937c52feb53b55c39e3f82094d27de464cDaniel EratWorkerPoolImpl::~WorkerPoolImpl() {
51b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pool_->Terminate();
52b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
53b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
54b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratvoid WorkerPoolImpl::PostTask(const tracked_objects::Location& from_here,
5560d96a4a9e0d8c78d46c1f86dec60f9ecdbedbdbChristopher Wiley                              const base::Closure& task,
5660d96a4a9e0d8c78d46c1f86dec60f9ecdbedbdbChristopher Wiley                              bool /* task_is_slow */) {
57b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pool_->PostTask(from_here, task);
58b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
59b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
60b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratbase::LazyInstance<WorkerPoolImpl> g_lazy_worker_pool =
61b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    LAZY_INSTANCE_INITIALIZER;
62b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
63b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratclass WorkerThread : public PlatformThread::Delegate {
64b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat public:
65b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  WorkerThread(const std::string& name_prefix,
66b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat               base::PosixDynamicThreadPool* pool)
67cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko      : name_prefix_(name_prefix), pool_(pool) {}
68b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
69b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  void ThreadMain() override;
70b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
71b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat private:
72b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  const std::string name_prefix_;
73b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  scoped_refptr<base::PosixDynamicThreadPool> pool_;
74b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
75b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  DISALLOW_COPY_AND_ASSIGN(WorkerThread);
76b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat};
77b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
78b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratvoid WorkerThread::ThreadMain() {
79b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  g_worker_pool_running_on_this_thread.Get().Set(true);
80cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko  const std::string name = base::StringPrintf("%s/%d", name_prefix_.c_str(),
81cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                                              PlatformThread::CurrentId());
82b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  // Note |name.c_str()| must remain valid for for the whole life of the thread.
83b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  PlatformThread::SetName(name);
84b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
85b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  for (;;) {
86b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    PendingTask pending_task = pool_->WaitForTask();
87b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    if (pending_task.task.is_null())
88b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      break;
89b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    TRACE_EVENT2("toplevel", "WorkerThread::ThreadMain::Run",
90b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat        "src_file", pending_task.posted_from.file_name(),
91b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat        "src_func", pending_task.posted_from.function_name());
92b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
93b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    tracked_objects::TaskStopwatch stopwatch;
94b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    stopwatch.Start();
95b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    pending_task.task.Run();
96b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    stopwatch.Stop();
97b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
98b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking(
99b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat        pending_task.birth_tally, pending_task.time_posted, stopwatch);
100b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  }
101b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
102b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  // The WorkerThread is non-joinable, so it deletes itself.
103b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  delete this;
104b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
105b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
106b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}  // namespace
107b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
108b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat// static
109b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratbool WorkerPool::PostTask(const tracked_objects::Location& from_here,
110cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                          const base::Closure& task,
111cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko                          bool task_is_slow) {
112b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow);
113b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  return true;
114b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
115b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
116b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat// static
117b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratbool WorkerPool::RunsTasksOnCurrentThread() {
118b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  return g_worker_pool_running_on_this_thread.Get().Get();
119b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
120b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
121b8cf94937c52feb53b55c39e3f82094d27de464cDaniel EratPosixDynamicThreadPool::PosixDynamicThreadPool(const std::string& name_prefix,
122b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat                                               int idle_seconds_before_exit)
123b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    : name_prefix_(name_prefix),
124b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      idle_seconds_before_exit_(idle_seconds_before_exit),
125b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      pending_tasks_available_cv_(&lock_),
126b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      num_idle_threads_(0),
127b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      terminated_(false) {}
128b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
129b8cf94937c52feb53b55c39e3f82094d27de464cDaniel EratPosixDynamicThreadPool::~PosixDynamicThreadPool() {
130b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  while (!pending_tasks_.empty())
131b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    pending_tasks_.pop();
132b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
133b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
134b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratvoid PosixDynamicThreadPool::Terminate() {
135b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  {
136b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    AutoLock locked(lock_);
137b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    DCHECK(!terminated_) << "Thread pool is already terminated.";
138b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    terminated_ = true;
139b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  }
140b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pending_tasks_available_cv_.Broadcast();
141b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
142b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
143b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratvoid PosixDynamicThreadPool::PostTask(
144b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    const tracked_objects::Location& from_here,
145b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    const base::Closure& task) {
146b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  PendingTask pending_task(from_here, task);
147b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  AddTask(&pending_task);
148b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
149b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
150b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Eratvoid PosixDynamicThreadPool::AddTask(PendingTask* pending_task) {
151b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  AutoLock locked(lock_);
152cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko  DCHECK(!terminated_)
153cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko      << "This thread pool is already terminated.  Do not post new tasks.";
154b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
155b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pending_tasks_.push(*pending_task);
156b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pending_task->task.Reset();
157b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
158b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  // We have enough worker threads.
159b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  if (static_cast<size_t>(num_idle_threads_) >= pending_tasks_.size()) {
160b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    pending_tasks_available_cv_.Signal();
161b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  } else {
162b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    // The new PlatformThread will take ownership of the WorkerThread object,
163b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    // which will delete itself on exit.
164cce46a0c214b37e8da48c522c83037e8ffa4f9fdAlex Vakulenko    WorkerThread* worker = new WorkerThread(name_prefix_, this);
165b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    PlatformThread::CreateNonJoinable(0, worker);
166b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  }
167b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
168b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
169b8cf94937c52feb53b55c39e3f82094d27de464cDaniel EratPendingTask PosixDynamicThreadPool::WaitForTask() {
170b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  AutoLock locked(lock_);
171b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
172b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  if (terminated_)
173b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    return PendingTask(FROM_HERE, base::Closure());
174b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
175b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  if (pending_tasks_.empty()) {  // No work available, wait for work.
176b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    num_idle_threads_++;
177b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    if (num_idle_threads_cv_.get())
178b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      num_idle_threads_cv_->Signal();
179b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    pending_tasks_available_cv_.TimedWait(
180b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat        TimeDelta::FromSeconds(idle_seconds_before_exit_));
181b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    num_idle_threads_--;
182b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    if (num_idle_threads_cv_.get())
183b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      num_idle_threads_cv_->Signal();
184b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    if (pending_tasks_.empty()) {
185b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      // We waited for work, but there's still no work.  Return NULL to signal
186b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      // the thread to terminate.
187b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat      return PendingTask(FROM_HERE, base::Closure());
188b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat    }
189b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  }
190b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
191b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  PendingTask pending_task = pending_tasks_.front();
192b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  pending_tasks_.pop();
193b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat  return pending_task;
194b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}
195b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat
196b8cf94937c52feb53b55c39e3f82094d27de464cDaniel Erat}  // namespace base
197