1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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_
|