Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/Scheduler.h

Issue 29370876: Issue #4692, #3595 - Stop sleeping in the implementation of `SetTimeout`
Patch Set: Created Jan. 9, 2017, 1:22 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/JsEngineInternal.h ('k') | src/Scheduler.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 #if !defined(ADBLOCKPLUS_SCHEDULER_H) 18 #if !defined(ADBLOCKPLUS_SCHEDULER_H)
19 #define ADBLOCKPLUS_SCHEDULER_H 19 #define ADBLOCKPLUS_SCHEDULER_H
20 20
21 #include <condition_variable> 21 #include <condition_variable>
22 #include <list> 22 #include <list>
23 #include <memory> 23 #include <memory>
24 #include <mutex> 24 #include <mutex>
25 #include <functional> 25 #include <functional>
26 #include <queue> 26 #include <queue>
27 #include <thread> 27 #include <thread>
28 28
29 namespace AdblockPlus
30 {
31 /*
32 * Adapter class for heap allocated function objects
33 *
34 * The legacy behavior of the original threading regime combined task life
35 * cycle and execution. Previously, tasks were allocated by the user and
36 * deleted at the end of execution internally. This adapter class allows
37 * separation of these concerns. It allows the legacy allocation behavior
38 * without burdening the new scheduler with any concern over task life cycle.
39 */
40 template<class T>
41 class HeapFunction
42 {
43 std::shared_ptr<T> body;
44
45 public:
46 HeapFunction(std::shared_ptr<T> body)
47 :body(body)
48 {}
49
50 void operator()()
51 {
52 if (body)
53 {
54 body->operator()();
55 }
56 }
57 };
58
59 /*
60 * Utility function uses type inference to simplify expressions at point of us e.
61 */
62 template<class T>
63 HeapFunction<T> MakeHeapFunction(std::shared_ptr<T> body)
64 {
65 return HeapFunction<T>(body);
66 }
67 }
68
69 /** 29 /**
70 * Interface class for scheduled tasks. 30 * Interface class for scheduled tasks.
71 */ 31 */
72 struct TaskFunctionInterface 32 struct TaskFunctionInterface
73 { 33 {
74 /** 34 /**
75 * The main function of the task, 35 * The main function of the task,
76 * the moral equivalent to the main function of the thread. 36 * the moral equivalent to the main function of the thread.
77 */ 37 */
78 virtual void operator()() = 0; 38 virtual void operator()() = 0;
79 /** 39 /**
80 * Request that the task end itself early, without needing to complete. 40 * Request that the task end itself early, without needing to complete.
81 * Reserved for future use. 41 * Reserved for future use.
82 */ 42 */
83 virtual void Interrupt() {}; 43 virtual void Interrupt() {};
84 }; 44 };
85 45
86 /** 46 /**
87 * Execute a task immediately in detached thread that's used only for this task.
88 *
89 * The present version is nothing more than a rewrite of the legacy behavior,
90 * which was to create a new thread for each task.
91 */
92 void StartImmediatelyInSingleUseDetachedThread(std::function<void()> task);
93
94 /**
95 * Active object to run arbitrary function objects. 47 * Active object to run arbitrary function objects.
96 * 48 *
97 * Operations execute in a thread owned by this instance. 49 * Operations execute in a thread owned by this instance.
98 * If a user wants to execute an operation in a different thread, 50 * If a user wants to execute an operation in a different thread,
99 * they have the responsibility to set up whatever additional 51 * they have the responsibility to set up whatever additional
100 * facilities may be required. 52 * facilities may be required.
101 */ 53 */
102 class OperationRunner 54 class OperationRunner
103 { 55 {
104 /** 56 /**
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 * 141 *
190 * The present version has some limitations and inefficiencies, 142 * The present version has some limitations and inefficiencies,
191 * trading them off for ease of incremental implementation. 143 * trading them off for ease of incremental implementation.
192 * - Separate schedulers for each `JsEngine` without any shared facilities. 144 * - Separate schedulers for each `JsEngine` without any shared facilities.
193 * - Working threads are single-use. No provision for a thread pool. 145 * - Working threads are single-use. No provision for a thread pool.
194 * - Tasks are simple function objects. Its possible to hasten termination 146 * - Tasks are simple function objects. Its possible to hasten termination
195 * in v8 by calling `TerminateExecution()`, for example, 147 * in v8 by calling `TerminateExecution()`, for example,
196 * but there's no interface for this. 148 * but there's no interface for this.
197 * - No support for delayed execution to support JavaScript `SetTimeout`. 149 * - No support for delayed execution to support JavaScript `SetTimeout`.
198 */ 150 */
199 template<class Worker> 151 template<class Worker, class UserData>
200 class SchedulerT 152 class SchedulerT
201 { 153 {
202 /** 154 /**
155 * A task as scheduled for execution.
156 *
157 * This class is a combination of two kinds of data:
158 * 1. An external object containing the body of the task.
159 * 2. All the internal elements required to run the task.
160 */
161 template<class UserData>
162 struct ScheduledTask
163 {
164 /**
165 * The external task.
166 * This member comes to us from our Run() method.
167 */
168 std::shared_ptr<TaskFunctionInterface> body;
169 /**
170 * The only internal data at present is the worker thread.
171 */
172 Worker worker;
173 /**
174 * Data associated with this task by the user of the scheduler
175 */
176 std::shared_ptr<UserData> userData;
177 /**
178 * Member worker is default-constructed
179 * because VS2012 insists on copy constructors when it doesn't need them.
180 */
181 ScheduledTask(std::shared_ptr<TaskFunctionInterface>&& body, std::shared_ptr <UserData>&& ud)
182 : body(std::forward<std::shared_ptr<TaskFunctionInterface>>(body)),
183 userData(std::forward<std::shared_ptr<UserData>>(ud))
184 {}
185 };
186
187 /**
203 * Shared mutex for all synchronization 188 * Shared mutex for all synchronization
204 */ 189 */
205 std::mutex m; 190 std::mutex m;
206 typedef std::unique_lock<std::mutex> UniqueLockType; 191 typedef std::unique_lock<std::mutex> UniqueLockType;
207 192
193 typedef std::list<ScheduledTask<UserData>> TaskListType;
208 /** 194 /**
209 * A list containing an entry for each running task. 195 * A list containing an entry for each running task.
210 */ 196 */
211 std::list<Worker> taskList; 197 TaskListType taskList;
212 198
213 /** 199 /**
214 * Condition variable notified when a running task is removed from its list 200 * Condition variable notified when a running task is removed from its list
215 */ 201 */
216 std::condition_variable taskRemoveCv; 202 std::condition_variable taskRemoveCv;
217 203
218 /** 204 /**
219 * Maintains a separate thread that is able to join completed worker threads. 205 * Maintains a separate thread that is able to join completed worker threads.
220 */ 206 */
221 OperationRunner monitor; 207 OperationRunner monitor;
222 208
223 /** 209 /**
210 * State flag.
211 *
212 * We only have two states: normal and terminating.
213 */
214 bool isNormal;
215
216 /**
224 * Execution wrapper for a task. 217 * Execution wrapper for a task.
225 * @param task 218 * @param task
226 * An iterator to the task within the task list. 219 * An iterator to the task within the task list.
227 */ 220 */
228 void WorkerMain(std::function<void()> body, typename std::list<Worker>::iterat or task) 221 void WorkerMain(typename TaskListType::iterator task)
229 { 222 {
230 body(); 223 (*task->body)();
231 monitor.Run([this, task]() { JoinAndRemove(task); }); 224 monitor.Run([this, task]() { JoinAndRemove(task); });
232 } 225 }
233 226
234 /** 227 /**
235 * Joins a task and then removes it from the task list. 228 * Joins a task and then removes it from the task list.
236 * @param task 229 * @param task
237 * An iterator to the task within the task list. 230 * An iterator to the task within the task list.
238 * 231 *
239 * This is the only function that may remove tasks from the running task list. 232 * This is the only function that may remove tasks from the running task list.
240 */ 233 */
241 void JoinAndRemove(typename std::list<Worker>::iterator task) 234 void JoinAndRemove(typename TaskListType::iterator task)
242 { 235 {
243 task->Join(); 236 task->worker.Join();
244 { 237 {
245 UniqueLockType ul(m); 238 UniqueLockType ul(m);
246 taskList.erase(task); 239 taskList.erase(task);
247 } 240 }
248 taskRemoveCv.notify_all(); 241 taskRemoveCv.notify_all();
249 } 242 }
250 243
251 public: 244 public:
252 SchedulerT() 245 SchedulerT()
246 : isNormal(true)
253 {} 247 {}
254 248
255 ~SchedulerT() 249 ~SchedulerT()
256 { 250 {
257 // `JoinAll()` may be called prior to destruction but, however, 251 // `JoinAll()` may be called prior to destruction but, however,
258 // there is no requirement that it be called beforehand. 252 // there is no requirement that it be called beforehand.
259 // We call it in the destructor to ensure all tasks have completed. 253 // We call it in the destructor to ensure all tasks have completed.
260 // If we do not, we risk destruction of a joinable thread, 254 // If we do not, we risk destruction of a joinable thread,
261 // which would generate a call to `std::terminate()`. 255 // which would generate a call to `std::terminate()`.
256 ShutDown();
262 JoinAll(); 257 JoinAll();
263 } 258 }
264 259
265 /** 260 /**
261 * Interrupt all pending tasks so that they'll terminate soon.
262 * Execution may proceed after a call to Interrupt(),
263 * but it does indicate that a task should terminate as soon as it can.
264 */
265 void ShutDown()
266 {
267 UniqueLockType ul(m);
268 // Ensure the task list does not grow during shutdown.
269 isNormal = false;
270
271 for (auto& it : taskList)
272 {
273 it.body->Interrupt();
274 }
275 }
276
277 /**
266 * Construct a `Worker` and run a task in it immediately. 278 * Construct a `Worker` and run a task in it immediately.
267 * 279 *
268 * This is the only function that may add tasks to the running task list. 280 * This is the only function that may add tasks to the running task list.
269 */ 281 */
270 void Run(std::function<void()> body) 282 void Run(std::shared_ptr<TaskFunctionInterface>&& body, std::shared_ptr<UserDa ta>&& ud)
271 { 283 {
272 UniqueLockType ul(m); 284 UniqueLockType ul(m);
285 if (!isNormal)
286 {
287 // We don't run new tasks if we're in a terminating state
288 return;
289 }
273 // Note that the interface to `Worker` uses a constructor and not a factory 290 // Note that the interface to `Worker` uses a constructor and not a factory
274 // Here we construct in place, avoiding both move and copy. 291 // Here we construct in place, avoiding both move and copy.
275 auto tt = taskList.emplace(taskList.begin()); 292 auto tt = taskList.emplace(taskList.begin(),
276 tt->Run([this, body, tt]() { WorkerMain(body, tt); }); 293 std::forward<std::shared_ptr<TaskFunctionInterface>>(body),
294 std::forward<std::shared_ptr<UserData>>(ud));
295 tt->worker.Run([this, tt]() { WorkerMain(tt); });
277 } 296 }
278 297
279 /** 298 /**
280 * Block until all running threads have been joined and the task list is empty . 299 * Block until all running threads have been joined and the task list is empty .
281 * 300 *
282 * This class does not take on any responsibility to keep the task list 301 * This class does not take on any responsibility to keep the task list
283 * from growing while this function executes. 302 * from growing while this function executes.
284 * Improper use can result in this function blocking forever; don't do that. 303 * Improper use can result in this function blocking forever; don't do that.
285 */ 304 */
286 void JoinAll() 305 void JoinAll()
287 { 306 {
288 // The call to `Worker::Join()` is in another function, 307 // The call to `Worker::Join()` is in another function,
289 // namely `JoinAndRemove()`, which also removes items from the task list. 308 // namely `JoinAndRemove()`, which also removes items from the task list.
290 // Thus all we do here is to wait for the task list to empty. 309 // Thus all we do here is to wait for the task list to empty.
291 UniqueLockType ul(m); 310 UniqueLockType ul(m);
292 taskRemoveCv.wait(ul, [this]() -> bool { return taskList.empty(); }); 311 taskRemoveCv.wait(ul, [this]() -> bool { return taskList.empty(); });
293 } 312 }
294 }; 313 };
295 314
296 #endif 315 #endif
OLDNEW
« no previous file with comments | « src/JsEngineInternal.h ('k') | src/Scheduler.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld