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

Side by Side Diff: test/PausePoint.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/Timeout.cpp ('k') | test/PausePoint.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(PAUSE_POINT_H)
19 #define PAUSE_POINT_H
20
21 #include <condition_variable>
22 #include <mutex>
23 #include <stdexcept>
24
25 /**
26 * An asymmetric rendezvous class
27 *
28 * This is a multithreading rendezvous class with assymmetric actors
29 * called "director" and "puppet".
30 * This class is designed for unit testing.
31 * In this case the director is the unit test itself
32 * and the puppets are other threads that the director spawns and controls.
33 * The goal this class supports is to tame non-deterministic execution order
34 * by serializing execution segments in different threads into a known order.
35 *
36 * This is a simple, two-party rendezvous.
37 * As with any rendezvous, the first party to hit a pause point blocks.
38 * When the second party hits the pause point, it blocks if needed,
39 * after which the director runs and the puppet blocks.
40 * The director must then explicitly unblock the puppet and allow it to run.
41 * By using multiple pause points in different threads,
42 * the director can create arbitrary execution order between its threads.
43 *
44 * See boost::barrier and boost::latch for other kinds of rendezvous classes.
45 * Note, however, that both of them are symmetric and not exact analogues.
46 *
47 * Please note this is a simple implementation, but sufficient for simple tests.
48 * It's not meant to be idiot-proof, nor of library quality.
49 * It is manifestly possible to write a unit test that hangs or deadlocks;
50 * it is the responsibility of the author to implement their tests correctly.
51 * One obvious deficiency is that it presents a single interface to two differen t roles.
52 *
53 * === Actors
54 * - director: calls WaitUntilPaused(), Resume()
55 * - puppet: calls Pause()
56 *
57 * === States
58 * - idle: puppet is not paused, director is not waiting
59 * - paused: puppet is paused, director is not waiting
60 * - waiting: director is waiting for the puppet to pause
61 *
62 * === Transitions
63 * - state idle
64 * - event pause
65 * - new state paused. Puppet call to Pause() block.
66 * - event wait
67 * - new state waiting. Director call to WaitUntilPaused() blocks.
68 * - event resume
69 * - ERROR. May not call Resume() when the puppet is not blocked.
70 * This class treats this case as a logic error, a flaw in the unit test
71 * - state paused
72 * - event pause
73 * - ERROR. May only call Pause() from the puppet thread.
74 * - event wait
75 * - Self-transition (state remains paused).
76 * This transition happens when the director calls WaitUntilPaused()
77 * while the puppet is paused.
78 * In this case the call returns immediately and does not block.
79 * - event resume
80 * - new state idle. Unblock puppet call to Pause().
81 * - state waiting
82 * - event pause
83 * - new state paused. Director call to WaitUntilPaused() unblocks.
84 * - event wait
85 * - ERROR. May only call WaitUntilPaused() from the director thread.
86 * - event resume
87 * - ERROR. May only call Resume() from the director thread.
88 */
89 class PausePoint
90 {
91 std::mutex m;
92 std::condition_variable cv;
93 typedef std::unique_lock<std::mutex> UniqueLockType;
94 enum State { Idle, Paused, Waiting };
95 State state;
96
97 public:
98 PausePoint();
99
100 /**
101 * Puppet: Pause here until the director orders us to resume.
102 */
103 void Pause();
104
105 /**
106 * Director: Wait here until the puppet hits the pause point.
107 */
108 void WaitUntilPaused();
109
110 /**
111 * Director: Order the puppet to resume execution.
112 */
113 void Resume();
114
115 /**
116 * Director: Call Resume() and then pause briefly.
117 *
118 * This function accommodates the case where we would prefer to pause executio n
119 * inside of a multiprocessing primitive such as `condition_variable::wait`.
120 * Since that's impossible, we pause as soon as we can before it and
121 * then resume with a tiny delay.
122 * This makes the puppet thread the only thread available for execution
123 * and we'll reach the blocking point at the primitive with high reliability .
124 * It's still possible that, for whatever reason, the puppet thread does not e xecute
125 * and we end up with a failed or hanging test, but that situation should be rare.
126 *
127 * The long-term solution to this problem requires "expanded" versions of prim itives
128 * that permit placing pause points on specific internal state transitions.
129 */
130 void ResumeAndSleep(unsigned int ms = 10);
131 };
132
133 /**
134 * A sentry that temporarily unlocks a mutex in the middle of a locked scope.
135 *
136 * \par Contract
137 * - Ownership of the constructor argument and any associated mutex transfer
138 * to this object for the duration of its life span.
139 * (If not, we might enable an exception thrown in the destructor.)
140 */
141 template<class L>
142 class AntiLock
143 {
144 L& lock;
145 public:
146 /**
147 * \par Precondition
148 * - argument lock must be locked
149 */
150 AntiLock(L& lock)
151 : lock(lock)
152 {
153 try
154 {
155 lock.unlock();
156 }
157 catch (...)
158 {
159 /*
160 * Any throw will be std::system_error, meaning the mutex was not locked.
161 * We want std::logic_error instead, since this means incorrect usage.
162 */
163 throw std::logic_error("Must construct AntiLock with a locked argument.");
164 }
165 };
166
167 ~AntiLock()
168 {
169 lock.lock();
170 };
171 };
172
173 #endif
OLDNEW
« no previous file with comments | « src/Timeout.cpp ('k') | test/PausePoint.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld