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

Side by Side Diff: src/Scheduler.h

Issue 29367507: Issue #3595 - Add an actual scheduler; use joined threads for file system
Patch Set: Created Dec. 14, 2016, 5:38 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
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>
22 #include <list>
21 #include <memory> 23 #include <memory>
24 #include <mutex>
22 #include <functional> 25 #include <functional>
26 #include <queue>
27 #include <thread>
23 28
24 namespace AdblockPlus 29 namespace AdblockPlus
25 { 30 {
26 /* 31 /*
27 * Adapter class for heap allocated function objects 32 * Adapter class for heap allocated function objects
28 * 33 *
29 * The legacy behavior of the original threading regime combined task life 34 * The legacy behavior of the original threading regime combined task life
30 * cycle and execution. Previously, tasks were allocated by the user and 35 * cycle and execution. Previously, tasks were allocated by the user and
31 * deleted at the end of execution internally. This adapter class allows 36 * deleted at the end of execution internally. This adapter class allows
32 * separation of these concerns. It allows the legacy allocation behavior 37 * separation of these concerns. It allows the legacy allocation behavior
(...skipping 19 matching lines...) Expand all
52 }; 57 };
53 58
54 /* 59 /*
55 * Utility function uses type inference to simplify expressions at point of us e. 60 * Utility function uses type inference to simplify expressions at point of us e.
56 */ 61 */
57 template<class T> 62 template<class T>
58 HeapFunction<T> MakeHeapFunction(std::shared_ptr<T> body) 63 HeapFunction<T> MakeHeapFunction(std::shared_ptr<T> body)
59 { 64 {
60 return HeapFunction<T>(body); 65 return HeapFunction<T>(body);
61 } 66 }
62
63 /*
64 * Scheduler for the execution of tasks.
65 *
66 * The present version is nothing more than a rewrite of the legacy behavior,
67 * which was to create a new thread for each task.
68 */
69 class Scheduler
70 {
71 public:
72 /*
73 * Execute a task immediately in a thread created for this task only
74 */
75 static void StartImmediatelyInSingleUseThread(std::function<void()> task);
76 };
77 } 67 }
68
69 /*
70 * Execute a task immediately in detached thread that's used only for this task.
71 *
72 * The present version is nothing more than a rewrite of the legacy behavior,
73 * which was to create a new thread for each task.
74 */
75 void StartImmediatelyInSingleUseDetachedThread(std::function<void()> task);
76
77 /**
78 * Active object to run arbitrary function objects.
79 *
80 * Operations execute in a thread owned by this instance.
81 * If a user wants to execute an operation in a different thread,
sergei 2017/01/20 13:08:14 We don't need this part "If a user wants to execut
Eric 2017/03/30 17:16:59 Documenting responsibilities is part of what makes
sergei 2017/04/03 15:35:33 Sorry, but I have not seen on the provisioned link
82 * they have the responsibility to set up whatever additional
83 * facilities may be required.
84 */
85 class OperationRunner
sergei 2017/01/20 13:08:14 Why not to call it ActiveObject?
Eric 2017/03/30 17:16:59 Because it's not a generic active object. It's tai
sergei 2017/04/03 15:35:33 Maybe it's not a generic active object but it is a
86 {
87 /**
88 * Shared mutex for all synchronization
89 */
90 std::mutex m;
91 typedef std::unique_lock<std::mutex> UniqueLockType;
92 /**
93 * Condition variable notified when a operation is added to its queue
94 */
95 std::condition_variable cv;
96 /**
97 * Operation queue for internal communication
98 */
99 std::queue<std::function<void()>> queue;
sergei 2017/01/20 13:08:14 Basically, it's a synchronized queue which has alr
Eric 2017/03/30 17:16:59 It's not a separate class because the mutex is use
sergei 2017/04/03 15:35:33 Could you please point to that race condition in t
100 /**
101 * The thread for the operation queue
102 */
103 std::thread th;
104 /**
105 * Flag to keep the operation queue running
106 */
107 bool isRunning;
108 /**
109 * Thread main for the operation queue thread.
110 */
111 void ThreadMain();
112
113 public:
114 /**
115 * Execution thread starts during construction.
116 */
117 OperationRunner();
118
119 /**
120 * Note: The execution thread must end before the destructor returns.
121 */
122 ~OperationRunner();
123
124 /**
125 * Queue up an operation for execution in the queue's thread.
126 */
127 void Run(std::function<void()> f);
128 };
129
130 /**
131 * A worker with a single-use-thread.
132 */
133 class SingleUseWorker
sergei 2017/01/20 13:08:13 It looks like there was a lot of discussions, fina
Eric 2017/03/30 17:16:59 It doesn't look from my perspective that there wer
sergei 2017/04/03 15:35:32 There were a lot of talks regarding designing a th
134 {
135 /**
136 * The underlying thread
137 */
138 std::thread th;
139
140 public:
141 /**
142 * Constructor does not start thread; that happens in `Run()`.
143 */
144 SingleUseWorker();
145 ~SingleUseWorker();
146 /**
147 * Run a task
148 *
149 * This class may rely on the restriction
150 * that the user may call this function at most once.
151 */
152 void Run(std::function<void()> f);
153 /**
154 * Synchronize with point of completion of task
155 *
156 * The present class calls `join()` on the thread,
157 * but alternates might synchronize in other ways.
158 */
159 void Join();
160 };
161
162 /**
163 * A simple scheduler.
164 *
165 * The scheduler keeps track of executing threads
166 * instead of detaching them and keeping no record of what's executing.
167 * The main goal at this stage is to be able to join all running threads.
168 * Since you can't call `join()` on a thread from within that thread,
169 * this class contains its a separate thread to be able to make such calls.
170 * This thread runs a generic operation queue
171 * that does more than simply join zombie threads.
172 *
sergei 2017/03/30 13:14:41 Could you please move that Scheduler with the func
Eric 2017/03/30 14:20:55 You might know what you want, but this description
sergei 2017/04/03 15:35:33 So, what are the difficulties to create a coderevi
173 * The present version has some limitations and inefficiencies,
174 * trading them off for ease of incremental implementation.
175 * - Separate schedulers for each `JsEngine` without any shared facilities.
sergei 2017/01/20 13:08:14 I would remove this string because whether there i
Eric 2017/03/30 17:16:58 Since you've not delved into this code the way I h
sergei 2017/04/03 15:35:33 I think that scheduler should not be so direct mem
176 * - Working threads are single-use. No provision for a thread pool.
sergei 2017/01/20 13:08:13 That's also questionable.
Eric 2017/03/30 17:16:59 It is absolutely not questionable that this class,
sergei 2017/04/03 15:35:33 Why should it have a thread pool? It's similar to
177 * - Tasks are simple function objects. Its possible to hasten termination
178 * in v8 by calling `TerminateExecution()`, for example,
179 * but there's no interface for this.
180 * - No support for delayed execution to support JavaScript `SetTimeout`.
181 */
182 template<class Worker>
183 class SchedulerT
184 {
185 /**
186 * Shared mutex for all synchronization
187 */
188 std::mutex m;
sergei 2017/01/20 13:08:14 it should be rather std::recursive_mutex because i
Eric 2017/03/30 17:16:58 No, it should not be recursive mutex. That would b
sergei 2017/04/03 15:35:33 Acknowledged.
189 typedef std::unique_lock<std::mutex> UniqueLockType;
190
191 /**
192 * A list containing an entry for each running task.
193 */
194 std::list<Worker> taskList;
195
196 /**
197 * Condition variable notified when a running task is removed from its list
198 */
199 std::condition_variable taskRemoveCv;
200
201 /**
202 * Maintains a separate thread that is able to join completed worker threads.
203 */
204 OperationRunner monitor;
205
206 /**
207 * Execution wrapper for a task.
208 * @param task
209 * An iterator to the task within the task list.
210 */
211 void WorkerMain(std::function<void()> body, typename std::list<Worker>::iterat or task)
212 {
213 body();
214 monitor.Run([this, task]() { JoinAndRemove(task); });
215 }
216
217 /**
218 * Joins a task and then removes it from the task list.
219 * @param task
220 * An iterator to the task within the task list.
221 *
222 * This is the only function that may remove tasks from the running task list.
223 */
224 void JoinAndRemove(typename std::list<Worker>::iterator task)
225 {
226 task->Join();
sergei 2017/04/03 15:35:33 here is a race condition, it can happen that move-
227 {
228 UniqueLockType ul(m);
229 taskList.erase(task);
230 }
231 taskRemoveCv.notify_all();
232 }
233
234 public:
235 SchedulerT()
236 {}
237
238 ~SchedulerT()
239 {
240 // `JoinAll()` may be called prior to destruction but, however,
241 // there is no requirement that it be called beforehand.
242 // We call it in the destructor to ensure all tasks have completed.
243 // If we do not, we risk destruction of a joinable thread,
244 // which would generate a call to `std::terminate()`.
245 JoinAll();
246 }
247
248 /**
249 * Construct a `Worker` and run a task in it immediately.
250 *
251 * This is the only function that may add tasks to the running task list.
252 */
253 void Run(std::function<void()> body)
sergei 2017/01/20 13:08:13 It would be easier to read if argument "body" is r
Eric 2017/03/30 17:16:58 I've used "body" consistently for a function that'
sergei 2017/04/03 15:35:33 I still think that another name is better here, bo
254 {
255 UniqueLockType ul(m);
256 // Note that the interface to `Worker` uses a constructor and not a factory
257 // Here we construct in place, avoiding both move and copy.
258 auto tt = taskList.emplace(taskList.begin());
259 tt->Run([this, body, tt]() { WorkerMain(body, tt); });
sergei 2017/01/20 13:08:13 I'm not sure that it's a wise decision to assume t
Eric 2017/03/30 17:16:58 This is not just an assumption. It's a design prin
sergei 2017/04/03 15:35:32 OK, let's say I want to implement timers using Set
260 }
261
262 /**
263 * Block until all running threads have been joined and the task list is empty .
264 *
265 * This class does not take on any responsibility to keep the task list
266 * from growing while this function executes.
267 * Improper use can result in this function blocking forever; don't do that.
268 */
269 void JoinAll()
270 {
271 // The call to `Worker::Join()` is in another function,
272 // namely `JoinAndRemove()`, which also removes items from the task list.
273 // Thus all we do here is to wait for the task list to empty.
274 UniqueLockType ul(m);
275 taskRemoveCv.wait(ul, [this]() -> bool { return taskList.empty(); });
276 }
277 };
278
78 #endif 279 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld