diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-01 05:42:34 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-01 05:42:34 +0000 |
commit | 098def22390dd628396e0d962235fc06e935282e (patch) | |
tree | b5fedd4b011fe5eabf61bc638c0287dddf074fb4 /base/threading/sequenced_worker_pool.h | |
parent | ed4c57531d269151dfad2f3aa3378ab43db562d0 (diff) | |
download | chromium_src-098def22390dd628396e0d962235fc06e935282e.zip chromium_src-098def22390dd628396e0d962235fc06e935282e.tar.gz chromium_src-098def22390dd628396e0d962235fc06e935282e.tar.bz2 |
Add a sequenced worker pool.
This allows tasks to be put in the worker pool with optional sequencing semantics for consumers that must run a bunch of stuff in order on a background thread, but don't particularly care about which thread.
Review URL: http://codereview.chromium.org/8416019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116078 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/threading/sequenced_worker_pool.h')
-rw-r--r-- | base/threading/sequenced_worker_pool.h | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h new file mode 100644 index 0000000..c6e0560 --- /dev/null +++ b/base/threading/sequenced_worker_pool.h @@ -0,0 +1,206 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREADING_SEQUENCED_WORKER_POOL_H_ +#define BASE_THREADING_SEQUENCED_WORKER_POOL_H_ +#pragma once + +#include <string> + +#include "base/callback.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/tracked_objects.h" +#include "base/base_export.h" + +namespace base { + +// A worker thread pool that enforces ordering between sets of tasks. It also +// allows you to specify what should happen to your tasks on shutdown. +// +// To enforce ordering, get a unique sequence token from the pool and post all +// tasks you want to order with the token. All tasks with the same token are +// guaranteed to execute serially, though not necessarily on the same thread. +// +// Example: +// SequencedWorkerPool::SequenceToken token = pool.GetSequenceToken(); +// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN, +// FROM_HERE, base::Bind(...)); +// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN, +// FROM_HERE, base::Bind(...)); +// +// You can make named sequence tokens to make it easier to share a token +// across different components. +// +// You can also post tasks to the pool without ordering using PostWorkerTask. +// These will be executed in an unspecified order. The order of execution +// between tasks with different sequence tokens is also unspecified. +// +// This class is designed to be leaked on shutdown to allow the +// CONTINUE_ON_SHUTDOWN behavior to be implemented. To enforce the +// BLOCK_SHUTDOWN behavior, you must call Shutdown() which will wait until +// the necessary tasks have completed. +// +// Implementation note: This does not use a base::WorkerPool since that does +// not enforce shutdown semantics or allow us to specify how many worker +// threads to run. For the typical use case of random background work, we don't +// necessarily want to be super aggressive about creating threads. +class BASE_EXPORT SequencedWorkerPool { + public: + // Defines what should happen to a task posted to the worker pool on shutdown. + enum WorkerShutdown { + // Tasks posted with this mode which have not run at shutdown will be + // deleted rather than run, and any tasks with this mode running at + // shutdown will be ignored (the worker thread will not be joined). + // + // This option provides a nice way to post stuff you don't want blocking + // shutdown. For example, you might be doing a slow DNS lookup and if it's + // blocked on the OS, you may not want to stop shutdown, since the result + // doesn't really matter at that point. + // + // However, you need to be very careful what you do in your callback when + // you use this option. Since the thread will continue to run until the OS + // terminates the process, the app can be in the process of tearing down + // when you're running. This means any singletons or global objects you + // use may suddenly become invalid out from under you. For this reason, + // it's best to use this only for slow but simple operations like the DNS + // example. + CONTINUE_ON_SHUTDOWN, + + // Tasks posted with this mode that have not started executing at shutdown + // will be deleted rather than executed. However, tasks already in progress + // will be completed. + SKIP_ON_SHUTDOWN, + + // Tasks posted with this mode will block browser shutdown until they're + // executed. Since this can have significant performance implications, use + // sparingly. + // + // Generally, this should be used only for user data, for example, a task + // writing a preference file. + // + // If a task is posted during shutdown, it will not get run since the + // workers may already be stopped. In this case, the post operation will + // fail (return false) and the task will be deleted. + BLOCK_SHUTDOWN, + }; + + // Opaque identifier that defines sequencing of tasks posted to the worker + // pool. See NewSequenceToken(). + class SequenceToken { + public: + explicit SequenceToken() : id_(0) {} + ~SequenceToken() {} + + bool Equals(const SequenceToken& other) const { + return id_ == other.id_; + } + + private: + friend class SequencedWorkerPool; + + SequenceToken(int id) : id_(id) {} + + int id_; + }; + + // Allows tests to perform certain actions. + class TestingObserver { + public: + virtual ~TestingObserver() {} + virtual void WillWaitForShutdown() = 0; + }; + + // Pass the maximum number of threads (they will be lazily created as needed) + // and a prefix for the thread name to ad in debugging. + SequencedWorkerPool(size_t max_threads, + const std::string& thread_name_prefix); + ~SequencedWorkerPool(); + + // Returns a unique token that can be used to sequence tasks posted to + // PostSequencedWorkerTask(). Valid tokens are alwys nonzero. + SequenceToken GetSequenceToken(); + + // Returns the sequence token associated with the given name. Calling this + // function multiple times with the same string will always produce the + // same sequence token. If the name has not been used before, a new token + // will be created. + SequenceToken GetNamedSequenceToken(const std::string& name); + + // Posts the given task for execution in the worker pool. Tasks posted with + // this function will execute in an unspecified order on a background thread. + // Returns true if the task was posted. If your tasks have ordering + // requirements, see PostSequencedWorkerTask(). + // + // This class will attempt to delete tasks that aren't run + // (non-block-shutdown semantics) but can't guarantee that this happens. If + // all worker threads are busy running CONTINUE_ON_SHUTDOWN tasks, there + // will be no workers available to delete these tasks. And there may be + // tasks with the same sequence token behind those CONTINUE_ON_SHUTDOWN + // tasks. Deleting those tasks before the previous one has completed could + // cause nondeterministic crashes because the task could be keeping some + // objects alive which do work in their destructor, which could voilate the + // assumptions of the running task. + // + // The task will be guaranteed to run to completion before shutdown + // (BLOCK_SHUTDOWN semantics). + // + // Returns true if the task was posted successfully. This may fail during + // shutdown regardless of the specified ShutdownBehavior. + bool PostWorkerTask(const tracked_objects::Location& from_here, + const base::Closure& task); + + // Same as PostWorkerTask but allows specification of the shutdown behavior. + bool PostWorkerTaskWithShutdownBehavior( + const tracked_objects::Location& from_here, + const base::Closure& task, + WorkerShutdown shutdown_behavior); + + // Like PostWorkerTask above, but provides sequencing semantics. This means + // that tasks posted with the same sequence token (see GetSequenceToken()) + // are guaranteed to execute in order. This is useful in cases where you're + // doing operations that may depend on previous ones, like appending to a + // file. + // + // The task will be guaranteed to run to completion before shutdown + // (BLOCK_SHUTDOWN semantics). + // + // Returns true if the task was posted successfully. This may fail during + // shutdown regardless of the specified ShutdownBehavior. + bool PostSequencedWorkerTask(SequenceToken sequence_token, + const tracked_objects::Location& from_here, + const base::Closure& task); + + // Same as PostSequencedWorkerTask but allows specification of the shutdown + // behavior. + bool PostSequencedWorkerTaskWithShutdownBehavior( + SequenceToken sequence_token, + const tracked_objects::Location& from_here, + const base::Closure& task, + WorkerShutdown shutdown_behavior); + + // Implements the worker pool shutdown. This should be called during app + // shutdown, and will discard/join with appropriate tasks before returning. + // After this call, subsequent calls to post tasks will fail. + void Shutdown(); + + // Called by tests to set the testing observer. This is NULL by default + // and ownership of the pointer is kept with the caller. + void SetTestingObserver(TestingObserver* observer); + + private: + class Inner; + class Worker; + + friend class Inner; + friend class Worker; + + scoped_refptr<Inner> inner_; + + DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPool); +}; + +} // namespace base + +#endif // BASE_THREADING_SEQUENCED_WORKER_POOL_H_ |