security/sandbox/chromium/base/threading/sequenced_worker_pool.h

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #ifndef BASE_THREADING_SEQUENCED_WORKER_POOL_H_
michael@0 6 #define BASE_THREADING_SEQUENCED_WORKER_POOL_H_
michael@0 7
michael@0 8 #include <cstddef>
michael@0 9 #include <string>
michael@0 10
michael@0 11 #include "base/base_export.h"
michael@0 12 #include "base/basictypes.h"
michael@0 13 #include "base/callback_forward.h"
michael@0 14 #include "base/memory/ref_counted.h"
michael@0 15 #include "base/memory/scoped_ptr.h"
michael@0 16 #include "base/task_runner.h"
michael@0 17
michael@0 18 namespace tracked_objects {
michael@0 19 class Location;
michael@0 20 } // namespace tracked_objects
michael@0 21
michael@0 22 namespace base {
michael@0 23
michael@0 24 class MessageLoopProxy;
michael@0 25
michael@0 26 template <class T> class DeleteHelper;
michael@0 27
michael@0 28 class SequencedTaskRunner;
michael@0 29
michael@0 30 // A worker thread pool that enforces ordering between sets of tasks. It also
michael@0 31 // allows you to specify what should happen to your tasks on shutdown.
michael@0 32 //
michael@0 33 // To enforce ordering, get a unique sequence token from the pool and post all
michael@0 34 // tasks you want to order with the token. All tasks with the same token are
michael@0 35 // guaranteed to execute serially, though not necessarily on the same thread.
michael@0 36 // This means that:
michael@0 37 //
michael@0 38 // - No two tasks with the same token will run at the same time.
michael@0 39 //
michael@0 40 // - Given two tasks T1 and T2 with the same token such that T2 will
michael@0 41 // run after T1, then T2 will start after T1 is destroyed.
michael@0 42 //
michael@0 43 // - If T2 will run after T1, then all memory changes in T1 and T1's
michael@0 44 // destruction will be visible to T2.
michael@0 45 //
michael@0 46 // Example:
michael@0 47 // SequencedWorkerPool::SequenceToken token = pool.GetSequenceToken();
michael@0 48 // pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
michael@0 49 // FROM_HERE, base::Bind(...));
michael@0 50 // pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
michael@0 51 // FROM_HERE, base::Bind(...));
michael@0 52 //
michael@0 53 // You can make named sequence tokens to make it easier to share a token
michael@0 54 // across different components.
michael@0 55 //
michael@0 56 // You can also post tasks to the pool without ordering using PostWorkerTask.
michael@0 57 // These will be executed in an unspecified order. The order of execution
michael@0 58 // between tasks with different sequence tokens is also unspecified.
michael@0 59 //
michael@0 60 // This class may be leaked on shutdown to facilitate fast shutdown. The
michael@0 61 // expected usage, however, is to call Shutdown(), which correctly accounts
michael@0 62 // for CONTINUE_ON_SHUTDOWN behavior and is required for BLOCK_SHUTDOWN
michael@0 63 // behavior.
michael@0 64 //
michael@0 65 // Implementation note: This does not use a base::WorkerPool since that does
michael@0 66 // not enforce shutdown semantics or allow us to specify how many worker
michael@0 67 // threads to run. For the typical use case of random background work, we don't
michael@0 68 // necessarily want to be super aggressive about creating threads.
michael@0 69 //
michael@0 70 // Note that SequencedWorkerPool is RefCountedThreadSafe (inherited
michael@0 71 // from TaskRunner).
michael@0 72 class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
michael@0 73 public:
michael@0 74 // Defines what should happen to a task posted to the worker pool on
michael@0 75 // shutdown.
michael@0 76 enum WorkerShutdown {
michael@0 77 // Tasks posted with this mode which have not run at shutdown will be
michael@0 78 // deleted rather than run, and any tasks with this mode running at
michael@0 79 // shutdown will be ignored (the worker thread will not be joined).
michael@0 80 //
michael@0 81 // This option provides a nice way to post stuff you don't want blocking
michael@0 82 // shutdown. For example, you might be doing a slow DNS lookup and if it's
michael@0 83 // blocked on the OS, you may not want to stop shutdown, since the result
michael@0 84 // doesn't really matter at that point.
michael@0 85 //
michael@0 86 // However, you need to be very careful what you do in your callback when
michael@0 87 // you use this option. Since the thread will continue to run until the OS
michael@0 88 // terminates the process, the app can be in the process of tearing down
michael@0 89 // when you're running. This means any singletons or global objects you
michael@0 90 // use may suddenly become invalid out from under you. For this reason,
michael@0 91 // it's best to use this only for slow but simple operations like the DNS
michael@0 92 // example.
michael@0 93 CONTINUE_ON_SHUTDOWN,
michael@0 94
michael@0 95 // Tasks posted with this mode that have not started executing at
michael@0 96 // shutdown will be deleted rather than executed. However, any tasks that
michael@0 97 // have already begun executing when shutdown is called will be allowed
michael@0 98 // to continue, and will block shutdown until completion.
michael@0 99 //
michael@0 100 // Note: Because Shutdown() may block while these tasks are executing,
michael@0 101 // care must be taken to ensure that they do not block on the thread that
michael@0 102 // called Shutdown(), as this may lead to deadlock.
michael@0 103 SKIP_ON_SHUTDOWN,
michael@0 104
michael@0 105 // Tasks posted with this mode will block shutdown until they're
michael@0 106 // executed. Since this can have significant performance implications,
michael@0 107 // use sparingly.
michael@0 108 //
michael@0 109 // Generally, this should be used only for user data, for example, a task
michael@0 110 // writing a preference file.
michael@0 111 //
michael@0 112 // If a task is posted during shutdown, it will not get run since the
michael@0 113 // workers may already be stopped. In this case, the post operation will
michael@0 114 // fail (return false) and the task will be deleted.
michael@0 115 BLOCK_SHUTDOWN,
michael@0 116 };
michael@0 117
michael@0 118 // Opaque identifier that defines sequencing of tasks posted to the worker
michael@0 119 // pool.
michael@0 120 class SequenceToken {
michael@0 121 public:
michael@0 122 SequenceToken() : id_(0) {}
michael@0 123 ~SequenceToken() {}
michael@0 124
michael@0 125 bool Equals(const SequenceToken& other) const {
michael@0 126 return id_ == other.id_;
michael@0 127 }
michael@0 128
michael@0 129 // Returns false if current thread is executing an unsequenced task.
michael@0 130 bool IsValid() const {
michael@0 131 return id_ != 0;
michael@0 132 }
michael@0 133
michael@0 134 private:
michael@0 135 friend class SequencedWorkerPool;
michael@0 136
michael@0 137 explicit SequenceToken(int id) : id_(id) {}
michael@0 138
michael@0 139 int id_;
michael@0 140 };
michael@0 141
michael@0 142 // Allows tests to perform certain actions.
michael@0 143 class TestingObserver {
michael@0 144 public:
michael@0 145 virtual ~TestingObserver() {}
michael@0 146 virtual void OnHasWork() = 0;
michael@0 147 virtual void WillWaitForShutdown() = 0;
michael@0 148 virtual void OnDestruct() = 0;
michael@0 149 };
michael@0 150
michael@0 151 // Gets the SequencedToken of the current thread.
michael@0 152 // If current thread is not a SequencedWorkerPool worker thread or is running
michael@0 153 // an unsequenced task, returns an invalid SequenceToken.
michael@0 154 static SequenceToken GetSequenceTokenForCurrentThread();
michael@0 155
michael@0 156 // When constructing a SequencedWorkerPool, there must be a
michael@0 157 // MessageLoop on the current thread unless you plan to deliberately
michael@0 158 // leak it.
michael@0 159
michael@0 160 // Pass the maximum number of threads (they will be lazily created as needed)
michael@0 161 // and a prefix for the thread name to aid in debugging.
michael@0 162 SequencedWorkerPool(size_t max_threads,
michael@0 163 const std::string& thread_name_prefix);
michael@0 164
michael@0 165 // Like above, but with |observer| for testing. Does not take
michael@0 166 // ownership of |observer|.
michael@0 167 SequencedWorkerPool(size_t max_threads,
michael@0 168 const std::string& thread_name_prefix,
michael@0 169 TestingObserver* observer);
michael@0 170
michael@0 171 // Returns a unique token that can be used to sequence tasks posted to
michael@0 172 // PostSequencedWorkerTask(). Valid tokens are always nonzero.
michael@0 173 SequenceToken GetSequenceToken();
michael@0 174
michael@0 175 // Returns the sequence token associated with the given name. Calling this
michael@0 176 // function multiple times with the same string will always produce the
michael@0 177 // same sequence token. If the name has not been used before, a new token
michael@0 178 // will be created.
michael@0 179 SequenceToken GetNamedSequenceToken(const std::string& name);
michael@0 180
michael@0 181 // Returns a SequencedTaskRunner wrapper which posts to this
michael@0 182 // SequencedWorkerPool using the given sequence token. Tasks with nonzero
michael@0 183 // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
michael@0 184 // are posted with BLOCK_SHUTDOWN behavior.
michael@0 185 scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunner(
michael@0 186 SequenceToken token);
michael@0 187
michael@0 188 // Returns a SequencedTaskRunner wrapper which posts to this
michael@0 189 // SequencedWorkerPool using the given sequence token. Tasks with nonzero
michael@0 190 // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
michael@0 191 // are posted with the given shutdown behavior.
michael@0 192 scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunnerWithShutdownBehavior(
michael@0 193 SequenceToken token,
michael@0 194 WorkerShutdown shutdown_behavior);
michael@0 195
michael@0 196 // Returns a TaskRunner wrapper which posts to this SequencedWorkerPool using
michael@0 197 // the given shutdown behavior. Tasks with nonzero delay are posted with
michael@0 198 // SKIP_ON_SHUTDOWN behavior and tasks with zero delay are posted with the
michael@0 199 // given shutdown behavior.
michael@0 200 scoped_refptr<TaskRunner> GetTaskRunnerWithShutdownBehavior(
michael@0 201 WorkerShutdown shutdown_behavior);
michael@0 202
michael@0 203 // Posts the given task for execution in the worker pool. Tasks posted with
michael@0 204 // this function will execute in an unspecified order on a background thread.
michael@0 205 // Returns true if the task was posted. If your tasks have ordering
michael@0 206 // requirements, see PostSequencedWorkerTask().
michael@0 207 //
michael@0 208 // This class will attempt to delete tasks that aren't run
michael@0 209 // (non-block-shutdown semantics) but can't guarantee that this happens. If
michael@0 210 // all worker threads are busy running CONTINUE_ON_SHUTDOWN tasks, there
michael@0 211 // will be no workers available to delete these tasks. And there may be
michael@0 212 // tasks with the same sequence token behind those CONTINUE_ON_SHUTDOWN
michael@0 213 // tasks. Deleting those tasks before the previous one has completed could
michael@0 214 // cause nondeterministic crashes because the task could be keeping some
michael@0 215 // objects alive which do work in their destructor, which could voilate the
michael@0 216 // assumptions of the running task.
michael@0 217 //
michael@0 218 // The task will be guaranteed to run to completion before shutdown
michael@0 219 // (BLOCK_SHUTDOWN semantics).
michael@0 220 //
michael@0 221 // Returns true if the task was posted successfully. This may fail during
michael@0 222 // shutdown regardless of the specified ShutdownBehavior.
michael@0 223 bool PostWorkerTask(const tracked_objects::Location& from_here,
michael@0 224 const Closure& task);
michael@0 225
michael@0 226 // Same as PostWorkerTask but allows a delay to be specified (although doing
michael@0 227 // so changes the shutdown behavior). The task will be run after the given
michael@0 228 // delay has elapsed.
michael@0 229 //
michael@0 230 // If the delay is nonzero, the task won't be guaranteed to run to completion
michael@0 231 // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
michael@0 232 // If the delay is zero, this behaves exactly like PostWorkerTask, i.e. the
michael@0 233 // task will be guaranteed to run to completion before shutdown
michael@0 234 // (BLOCK_SHUTDOWN semantics).
michael@0 235 bool PostDelayedWorkerTask(const tracked_objects::Location& from_here,
michael@0 236 const Closure& task,
michael@0 237 TimeDelta delay);
michael@0 238
michael@0 239 // Same as PostWorkerTask but allows specification of the shutdown behavior.
michael@0 240 bool PostWorkerTaskWithShutdownBehavior(
michael@0 241 const tracked_objects::Location& from_here,
michael@0 242 const Closure& task,
michael@0 243 WorkerShutdown shutdown_behavior);
michael@0 244
michael@0 245 // Like PostWorkerTask above, but provides sequencing semantics. This means
michael@0 246 // that tasks posted with the same sequence token (see GetSequenceToken())
michael@0 247 // are guaranteed to execute in order. This is useful in cases where you're
michael@0 248 // doing operations that may depend on previous ones, like appending to a
michael@0 249 // file.
michael@0 250 //
michael@0 251 // The task will be guaranteed to run to completion before shutdown
michael@0 252 // (BLOCK_SHUTDOWN semantics).
michael@0 253 //
michael@0 254 // Returns true if the task was posted successfully. This may fail during
michael@0 255 // shutdown regardless of the specified ShutdownBehavior.
michael@0 256 bool PostSequencedWorkerTask(SequenceToken sequence_token,
michael@0 257 const tracked_objects::Location& from_here,
michael@0 258 const Closure& task);
michael@0 259
michael@0 260 // Like PostSequencedWorkerTask above, but allows you to specify a named
michael@0 261 // token, which saves an extra call to GetNamedSequenceToken.
michael@0 262 bool PostNamedSequencedWorkerTask(const std::string& token_name,
michael@0 263 const tracked_objects::Location& from_here,
michael@0 264 const Closure& task);
michael@0 265
michael@0 266 // Same as PostSequencedWorkerTask but allows a delay to be specified
michael@0 267 // (although doing so changes the shutdown behavior). The task will be run
michael@0 268 // after the given delay has elapsed.
michael@0 269 //
michael@0 270 // If the delay is nonzero, the task won't be guaranteed to run to completion
michael@0 271 // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
michael@0 272 // If the delay is zero, this behaves exactly like PostSequencedWorkerTask,
michael@0 273 // i.e. the task will be guaranteed to run to completion before shutdown
michael@0 274 // (BLOCK_SHUTDOWN semantics).
michael@0 275 bool PostDelayedSequencedWorkerTask(
michael@0 276 SequenceToken sequence_token,
michael@0 277 const tracked_objects::Location& from_here,
michael@0 278 const Closure& task,
michael@0 279 TimeDelta delay);
michael@0 280
michael@0 281 // Same as PostSequencedWorkerTask but allows specification of the shutdown
michael@0 282 // behavior.
michael@0 283 bool PostSequencedWorkerTaskWithShutdownBehavior(
michael@0 284 SequenceToken sequence_token,
michael@0 285 const tracked_objects::Location& from_here,
michael@0 286 const Closure& task,
michael@0 287 WorkerShutdown shutdown_behavior);
michael@0 288
michael@0 289 // TaskRunner implementation. Forwards to PostDelayedWorkerTask().
michael@0 290 virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
michael@0 291 const Closure& task,
michael@0 292 TimeDelta delay) OVERRIDE;
michael@0 293 virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
michael@0 294
michael@0 295 // Returns true if the current thread is processing a task with the given
michael@0 296 // sequence_token.
michael@0 297 bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
michael@0 298
michael@0 299 // Blocks until all pending tasks are complete. This should only be called in
michael@0 300 // unit tests when you want to validate something that should have happened.
michael@0 301 // This will not flush delayed tasks; delayed tasks get deleted.
michael@0 302 //
michael@0 303 // Note that calling this will not prevent other threads from posting work to
michael@0 304 // the queue while the calling thread is waiting on Flush(). In this case,
michael@0 305 // Flush will return only when there's no more work in the queue. Normally,
michael@0 306 // this doesn't come up since in a test, all the work is being posted from
michael@0 307 // the main thread.
michael@0 308 void FlushForTesting();
michael@0 309
michael@0 310 // Spuriously signal that there is work to be done.
michael@0 311 void SignalHasWorkForTesting();
michael@0 312
michael@0 313 // Implements the worker pool shutdown. This should be called during app
michael@0 314 // shutdown, and will discard/join with appropriate tasks before returning.
michael@0 315 // After this call, subsequent calls to post tasks will fail.
michael@0 316 //
michael@0 317 // Must be called from the same thread this object was constructed on.
michael@0 318 void Shutdown() { Shutdown(0); }
michael@0 319
michael@0 320 // A variant that allows an arbitrary number of new blocking tasks to
michael@0 321 // be posted during shutdown from within tasks that execute during shutdown.
michael@0 322 // Only tasks designated as BLOCKING_SHUTDOWN will be allowed, and only if
michael@0 323 // posted by tasks that are not designated as CONTINUE_ON_SHUTDOWN. Once
michael@0 324 // the limit is reached, subsequent calls to post task fail in all cases.
michael@0 325 //
michael@0 326 // Must be called from the same thread this object was constructed on.
michael@0 327 void Shutdown(int max_new_blocking_tasks_after_shutdown);
michael@0 328
michael@0 329 // Check if Shutdown was called for given threading pool. This method is used
michael@0 330 // for aborting time consuming operation to avoid blocking shutdown.
michael@0 331 //
michael@0 332 // Can be called from any thread.
michael@0 333 bool IsShutdownInProgress();
michael@0 334
michael@0 335 protected:
michael@0 336 virtual ~SequencedWorkerPool();
michael@0 337
michael@0 338 virtual void OnDestruct() const OVERRIDE;
michael@0 339
michael@0 340 private:
michael@0 341 friend class RefCountedThreadSafe<SequencedWorkerPool>;
michael@0 342 friend class DeleteHelper<SequencedWorkerPool>;
michael@0 343
michael@0 344 class Inner;
michael@0 345 class Worker;
michael@0 346
michael@0 347 const scoped_refptr<MessageLoopProxy> constructor_message_loop_;
michael@0 348
michael@0 349 // Avoid pulling in too many headers by putting (almost) everything
michael@0 350 // into |inner_|.
michael@0 351 const scoped_ptr<Inner> inner_;
michael@0 352
michael@0 353 DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPool);
michael@0 354 };
michael@0 355
michael@0 356 } // namespace base
michael@0 357
michael@0 358 #endif // BASE_THREADING_SEQUENCED_WORKER_POOL_H_

mercurial