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

Side by Side Diff: src/Timeout.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/Scheduler.cpp ('k') | src/Timeout.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 Eyeo GmbH
4 *
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
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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/>.
16 */
17
18 #if !defined(TIMEOUT_H)
19 #define TIMEOUT_H
20
21 #include <chrono>
22 #include <condition_variable>
23 #include <mutex>
24 #include <v8.h>
25
26 /**
27 * Callback function implementing "SetTimeout()" on the global JS object.
28 */
29 v8::Handle<v8::Value> CallbackForSetTimeout(const v8::Arguments& arguments);
30
31 /**
32 * Base class for aspect classes.
33 * Provides uniform accessor to the aspect.
34 * Instantiate using CRTP.
35 */
36 template<class AspectType>
37 class AspectBase
38 {
39 public:
40 AspectType& Aspect()
41 {
42 return *static_cast<AspectType*>(this);
43 }
44 };
45
46 /**
47 * Definitions for all aspect points used in DelayedExecutorAspected.
48 */
49 struct DelayedExecutorAspectPoints
50 {
51 template<class LockType>
52 void RunnableToSleeping(LockType& lock) {};
53
54 void RunFinish() {};
55 };
56
57 class DelayedExecutorNullAspect
58 : public AspectBase<DelayedExecutorNullAspect>,
59 public DelayedExecutorAspectPoints
60 {};
61
62 /**
63 * Execute a function after a certain time has elapsed.
64 * Or don't execute it if you change your mind beforehand.
65 *
66 * This is the functional equivalent of sleeping-then-executing,
67 * with the exception that we can wake up the sleep early and stop execution.
68 */
69 template<class AspectType>
70 class DelayedExecutorAspected
71 : protected AspectType
72 {
73 enum State
74 {
75 Runnable, ///< The state at construction. Awaiting a command to run.
76 Sleeping, ///< Ordered to run, but the delay timer is still ticking.
77 Interrupted, ///< Sleep was interrupted
78 Running, ///< In the middle of execution.
79 Completed, ///< Execution completed.
80 Error ///< An error encountered anywhere. Disables all operations.
81 };
82
83 /**
84 * Internal state.
85 *
86 * \par State Machine
87 * - Initial state is always "Runnable".
88 * - Final states are "Completed" and "Error".
89 * - Every non-final state may transition to "Error".
90 * - The ordinary linear sequence:
91 * "Runnable" --> "Sleeping" --> "Running" --> "Completed".
92 * - Accelerated execution:
93 * "Runnable" --> "Running"
94 * - The interruption transitions:
95 * "Sleeping" --> "Interrupted" --> "Completed"
96 */
97 State state;
98
99 /**
100 * Mutex primarily protects the internal state machine.
101 */
102 std::mutex m;
103 /**
104 * Condition variable to wait on instead of sleeping.
105 */
106 std::condition_variable cv;
107 /**
108 * Lock type is unique for use with condition variable.
109 */
110 typedef std::unique_lock<std::mutex> LockType;
111 /**
112 * The function that may execute, or not.
113 */
114 std::function<void()> body;
115
116 /**
117 * Validation routine.
118 */
119 void AssertNotInErrorState()
120 {
121 if (state == Error)
122 {
123 throw std::runtime_error("May not perform operation in error state");
124 }
125 }
126
127 /**
128 * Run the body unconditionally.
129 * This function does not take responsibility for prior state validation.
130 */
131 void Run()
132 {
133 try
134 {
135 // Run body with mutex unlocked
136 body();
137 // Now that body has completed successfully we can change state
138 LockType lock(m);
139 state = Completed;
140 }
141 catch (...)
142 {
143 LockType lock(m);
144 state = Error;
145 }
146 Aspect().RunFinish();
147 }
148
149 public:
150 /**
151 * Constructor starts the delay timer.
152 * If the delay is zero, execution begins immediately.
153 *
154 * @param body
155 * A function object to execute. May not be empty.
156 */
157 DelayedExecutorAspected(AspectType&& aspect, std::function<void()> body)
158 : AspectType(std::move(aspect)), state(Runnable), body(body)
159 {
160 if (!body)
161 {
162 throw std::logic_error("Body function may not be empty");
163 }
164 }
165
166 /**
167 * Run the function body immediately and block until it's done.
168 *
169 * If we are not runnable, this is an error.
170 */
171 void RunImmediately()
172 {
173 // Validate state and transition with mutex protection
174 {
175 LockType lock(m);
176 switch (state)
177 {
178 case Runnable:
179 case Sleeping:
180 state = Running;
181 break;
182 case Interrupted:
183 case Running:
184 case Completed:
185 throw std::runtime_error("May not run immediately unless state is runnab le or sleeping");
186 case Error:
187 throw std::runtime_error("May not run immediately from the error state") ;
188 default:
189 throw std::logic_error("Unknown state");
190 }
191 }
192 // Run with mutex unlocked
193 Run();
194 }
195
196 /**
197 * Run the body after some period of time has elapsed.
198 */
199 template <class Rep, class Period>
200 void RunWithDelay(const std::chrono::duration<Rep, Period>& duration)
201 {
202 // Validate state and transition with mutex protection
203 {
204 LockType lock(m);
205 switch (state)
206 {
207 case Runnable:
208 state = Sleeping;
209 break;
210 case Sleeping:
211 case Interrupted:
212 case Running:
213 case Completed:
214 throw std::runtime_error("May not run with delay unless state is runnabl e");
215 case Error:
216 throw std::runtime_error("May not run with delay from the error state");
217 default:
218 throw std::logic_error("Unknown state");
219 }
220
221 /*
222 * Aspect point for beginning of sleep.
223 * Ideally, this point would be inside the condition variable as it
224 * transitions to waiting, immediately after it unlocks its mutex.
225 * Putting it right before the statement is a reasonable substitute.
226 * Note: aspect should unlock and re-lock mutex
227 */
228 Aspect().RunnableToSleeping(lock);
229
230 auto completed = [this]() -> bool
231 {
232 return state != Sleeping;
233 };
234 // Assert state == Sleeping
235 cv.wait_for(lock, duration, completed);
236 // Assert either the wait timed out or we are no longer in the sleeping st ate
237
238 switch (state)
239 {
240 case Sleeping:
241 // We timed out and sleep was completed. Time to run the body.
242 state = Running;
243 break;
244 case Interrupted:
245 // We were interrupted while sleeping, so we're all done.
246 state = Completed;
247 return;
248 default:
249 throw std::runtime_error("Unexpected state after finished sleeping");
250 }
251 }
252 // Assert state == Running
253 Run();
254 }
255
256 /**
257 * Stave off running our function body if that's still possible.
258 *
259 * If we are "Running", return false.
260 * If we are "Runnable" or "Sleeping", then we transition to "Completed"
261 * and return true; in this case the body never executes.
262 * If we are "Completed", we simply return true.
263 *
264 * @return
265 * - True if we have verified that execution has completed.
266 * - False if we cannot verify that execution has completed.
267 * - Note that we can return false here but execution can complete on its
268 * own before we receive our next call.
269 */
270 bool ForestallExecution()
271 {
272 LockType lock(m);
273 switch (state)
274 {
275 case Runnable:
276 case Sleeping:
277 state = Interrupted;
278 // fall through
279 case Interrupted: // permissible, but usually irrelevant
280 cv.notify_one();
281 return true;
282 case Running:
283 case Completed:
284 return false;
285 case Error:
286 throw std::runtime_error("May not forestall execution from the error state ");
287 default:
288 throw std::logic_error("Unknown state");
289 }
290 }
291 };
292
293 class DelayedExecutor
294 : public DelayedExecutorAspected<DelayedExecutorNullAspect>
295 {
296 public:
297 typedef DelayedExecutorAspected<DelayedExecutorNullAspect> Base;
298 DelayedExecutor(std::function<void()> body)
299 : Base(DelayedExecutorNullAspect(), body)
300 {}
301 };
302
303 #endif
OLDNEW
« no previous file with comments | « src/Scheduler.cpp ('k') | src/Timeout.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld