Merge lp:~michihenning/unity-scopes-api/signal-before-unlock into lp:unity-scopes-api
- signal-before-unlock
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michi Henning |
Approved revision: | 41 |
Merged at revision: | 41 |
Proposed branch: | lp:~michihenning/unity-scopes-api/signal-before-unlock |
Merge into: | lp:unity-scopes-api |
Diff against target: |
321 lines (+44/-77) 8 files modified
demo/client.cpp (+2/-4) demo/scope-C.cpp (+5/-10) demo/scope-D.cpp (+5/-10) src/unity/api/scopes/internal/Reaper.cpp (+5/-7) src/unity/api/scopes/internal/ReplyObject.cpp (+0/-1) src/unity/api/scopes/internal/ScopeLoader.cpp (+27/-41) src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp (+0/-2) src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp (+0/-2) |
To merge this branch: | bzr merge lp:~michihenning/unity-scopes-api/signal-before-unlock |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marcus Tomlinson (community) | Approve | ||
Review via email:
|
Commit message
Changed code to signal condvar while still holding the lock. No correctness issue here. The change is mainly to stop warnings from helgrind, which doesn't like signals outside a lock.
Description of the change
Changed code to signal condvar while still holding the lock. No correctness issue here. The change is mainly to stop warnings from helgrind, which doesn't like signals outside a lock.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Marcus Tomlinson (marcustomlinson) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michi Henning (michihenning) wrote : | # |
Hmmm... Pretty much all the places where there is a notify or notify_all, that is immediately followed by a close of hte scope, a break, or a return, which all do the unlock.
Which unlock() calls would you restore and put after the notify?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Marcus Tomlinson (marcustomlinson) wrote : | # |
Ok sure, apart from the one removed from ScopeLoader.cpp, all mutexes are unlocked implicitly almost immediately. This is fine provided that the methods stay this way. If one of these methods were extended further, or a complex temporary were added and required clean up, we could cause the mutex to be held longer unintentionally in the future. Its your call really, just putting it out there :) The fix is correct and working.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Marcus Tomlinson (marcustomlinson) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michi Henning (michihenning) wrote : | # |
Well, if I add code later that depends on holding a lock, I need to examine the lock scopes anyway, just as I do if I add code later that shouldn't hold a lock. It's just something we need to think about when changing threaded code, no matter what convention we use to unlock.
The unlock() in ScopeLoader.cpp could stay and would release the mutex a little earlier. But then we end up with noise from helgrind again when the notify happens further down if we took the code path where the unlock() call was.
I think, on balance, it's probably better to leave it as is. At least until we find that it's broken :-)
Preview Diff
1 | === modified file 'demo/client.cpp' | |||
2 | --- demo/client.cpp 2013-09-23 06:13:06 +0000 | |||
3 | +++ demo/client.cpp 2013-10-25 04:17:24 +0000 | |||
4 | @@ -41,10 +41,8 @@ | |||
5 | 41 | virtual void finished() override | 41 | virtual void finished() override |
6 | 42 | { | 42 | { |
7 | 43 | cout << "query complete" << endl; | 43 | cout << "query complete" << endl; |
12 | 44 | { | 44 | unique_lock<decltype(mutex_)> lock(mutex_); |
13 | 45 | unique_lock<decltype(mutex_)> lock(mutex_); | 45 | query_complete_ = true; |
10 | 46 | query_complete_ = true; | ||
11 | 47 | } | ||
14 | 48 | condvar_.notify_one(); | 46 | condvar_.notify_one(); |
15 | 49 | } | 47 | } |
16 | 50 | 48 | ||
17 | 51 | 49 | ||
18 | === modified file 'demo/scope-C.cpp' | |||
19 | --- demo/scope-C.cpp 2013-08-28 06:26:46 +0000 | |||
20 | +++ demo/scope-C.cpp 2013-10-25 04:17:24 +0000 | |||
21 | @@ -44,10 +44,8 @@ | |||
22 | 44 | public: | 44 | public: |
23 | 45 | void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy) | 45 | void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy) |
24 | 46 | { | 46 | { |
29 | 47 | { | 47 | std::lock_guard<std::mutex> lock(mutex_); |
30 | 48 | std::lock_guard<std::mutex> lock(mutex_); | 48 | queries_.push_back(QueryData { query, query_string, reply_proxy }); |
27 | 49 | queries_.push_back(QueryData { query, query_string, reply_proxy }); | ||
28 | 50 | } | ||
31 | 51 | condvar_.notify_one(); | 49 | condvar_.notify_one(); |
32 | 52 | } | 50 | } |
33 | 53 | 51 | ||
34 | @@ -57,7 +55,6 @@ | |||
35 | 57 | condvar_.wait(lock, [this] { return !queries_.empty() || done_; }); | 55 | condvar_.wait(lock, [this] { return !queries_.empty() || done_; }); |
36 | 58 | if (done_) | 56 | if (done_) |
37 | 59 | { | 57 | { |
38 | 60 | lock.unlock(); | ||
39 | 61 | condvar_.notify_all(); | 58 | condvar_.notify_all(); |
40 | 62 | } | 59 | } |
41 | 63 | else | 60 | else |
42 | @@ -88,11 +85,9 @@ | |||
43 | 88 | 85 | ||
44 | 89 | void finish() | 86 | void finish() |
45 | 90 | { | 87 | { |
51 | 91 | { | 88 | std::unique_lock<std::mutex> lock(mutex_); |
52 | 92 | std::unique_lock<std::mutex> lock(mutex_); | 89 | queries_.clear(); |
53 | 93 | queries_.clear(); | 90 | done_ = true; |
49 | 94 | done_ = true; | ||
50 | 95 | } | ||
54 | 96 | condvar_.notify_all(); | 91 | condvar_.notify_all(); |
55 | 97 | } | 92 | } |
56 | 98 | 93 | ||
57 | 99 | 94 | ||
58 | === modified file 'demo/scope-D.cpp' | |||
59 | --- demo/scope-D.cpp 2013-08-28 06:26:46 +0000 | |||
60 | +++ demo/scope-D.cpp 2013-10-25 04:17:24 +0000 | |||
61 | @@ -47,10 +47,8 @@ | |||
62 | 47 | public: | 47 | public: |
63 | 48 | void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy) | 48 | void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy) |
64 | 49 | { | 49 | { |
69 | 50 | { | 50 | std::lock_guard<std::mutex> lock(mutex_); |
70 | 51 | std::lock_guard<std::mutex> lock(mutex_); | 51 | queries_.push_back(QueryData { query, query_string, reply_proxy }); |
67 | 52 | queries_.push_back(QueryData { query, query_string, reply_proxy }); | ||
68 | 53 | } | ||
71 | 54 | condvar_.notify_one(); | 52 | condvar_.notify_one(); |
72 | 55 | } | 53 | } |
73 | 56 | 54 | ||
74 | @@ -60,7 +58,6 @@ | |||
75 | 60 | condvar_.wait(lock, [this] { return !queries_.empty() || done_; }); | 58 | condvar_.wait(lock, [this] { return !queries_.empty() || done_; }); |
76 | 61 | if (done_) | 59 | if (done_) |
77 | 62 | { | 60 | { |
78 | 63 | lock.unlock(); | ||
79 | 64 | condvar_.notify_all(); | 61 | condvar_.notify_all(); |
80 | 65 | } | 62 | } |
81 | 66 | else | 63 | else |
82 | @@ -91,11 +88,9 @@ | |||
83 | 91 | 88 | ||
84 | 92 | void finish() | 89 | void finish() |
85 | 93 | { | 90 | { |
91 | 94 | { | 91 | std::unique_lock<std::mutex> lock(mutex_); |
92 | 95 | std::unique_lock<std::mutex> lock(mutex_); | 92 | queries_.clear(); |
93 | 96 | queries_.clear(); | 93 | done_ = true; |
89 | 97 | done_ = true; | ||
90 | 98 | } | ||
94 | 99 | condvar_.notify_all(); | 94 | condvar_.notify_all(); |
95 | 100 | } | 95 | } |
96 | 101 | 96 | ||
97 | 102 | 97 | ||
98 | === modified file 'src/unity/api/scopes/internal/Reaper.cpp' | |||
99 | --- src/unity/api/scopes/internal/Reaper.cpp 2013-08-27 06:28:14 +0000 | |||
100 | +++ src/unity/api/scopes/internal/Reaper.cpp 2013-10-25 04:17:24 +0000 | |||
101 | @@ -126,8 +126,8 @@ | |||
102 | 126 | { | 126 | { |
103 | 127 | lock_guard<mutex> lock(mutex_); | 127 | lock_guard<mutex> lock(mutex_); |
104 | 128 | finish_ = true; | 128 | finish_ = true; |
105 | 129 | do_work_.notify_one(); | ||
106 | 129 | } | 130 | } |
107 | 130 | do_work_.notify_one(); | ||
108 | 131 | reap_thread_.join(); | 131 | reap_thread_.join(); |
109 | 132 | } | 132 | } |
110 | 133 | 133 | ||
111 | @@ -161,17 +161,15 @@ | |||
112 | 161 | 161 | ||
113 | 162 | // Put new Item at the head of the list. | 162 | // Put new Item at the head of the list. |
114 | 163 | reaper_private::Reaplist::iterator ri; | 163 | reaper_private::Reaplist::iterator ri; |
115 | 164 | size_t list_size; | ||
116 | 165 | { | 164 | { |
117 | 166 | lock_guard<mutex> lock(mutex_); | 165 | lock_guard<mutex> lock(mutex_); |
118 | 167 | Item item(cb); | 166 | Item item(cb); |
119 | 168 | list_.push_front(item); // LRU order | 167 | list_.push_front(item); // LRU order |
120 | 169 | ri = list_.begin(); | 168 | ri = list_.begin(); |
126 | 170 | list_size = list_.size(); | 169 | if (list_.size() == 1) // List just became non-empty |
127 | 171 | } | 170 | { |
128 | 172 | if (list_size == 1) // List just became non-empty | 171 | do_work_.notify_one(); // Wake up reaper thread |
129 | 173 | { | 172 | } |
125 | 174 | do_work_.notify_one(); // Wake up reaper thread | ||
130 | 175 | } | 173 | } |
131 | 176 | 174 | ||
132 | 177 | // Make a new ReapItem. | 175 | // Make a new ReapItem. |
133 | 178 | 176 | ||
134 | === modified file 'src/unity/api/scopes/internal/ReplyObject.cpp' | |||
135 | --- src/unity/api/scopes/internal/ReplyObject.cpp 2013-08-27 06:28:14 +0000 | |||
136 | +++ src/unity/api/scopes/internal/ReplyObject.cpp 2013-10-25 04:17:24 +0000 | |||
137 | @@ -117,7 +117,6 @@ | |||
138 | 117 | lock.lock(); | 117 | lock.lock(); |
139 | 118 | if (--num_push_ == 0) | 118 | if (--num_push_ == 0) |
140 | 119 | { | 119 | { |
141 | 120 | lock.unlock(); | ||
142 | 121 | idle_.notify_one(); | 120 | idle_.notify_one(); |
143 | 122 | } | 121 | } |
144 | 123 | } | 122 | } |
145 | 124 | 123 | ||
146 | === modified file 'src/unity/api/scopes/internal/ScopeLoader.cpp' | |||
147 | --- src/unity/api/scopes/internal/ScopeLoader.cpp 2013-08-20 05:25:28 +0000 | |||
148 | +++ src/unity/api/scopes/internal/ScopeLoader.cpp 2013-10-25 04:17:24 +0000 | |||
149 | @@ -145,8 +145,8 @@ | |||
150 | 145 | // FALLTHROUGH | 145 | // FALLTHROUGH |
151 | 146 | case ScopeState::Finished: | 146 | case ScopeState::Finished: |
152 | 147 | { | 147 | { |
153 | 148 | cmd_changed_.notify_all(); | ||
154 | 148 | lock.unlock(); | 149 | lock.unlock(); |
155 | 149 | cmd_changed_.notify_all(); | ||
156 | 150 | if (run_thread_.joinable()) // If unload is called more than once, don't join a second time. | 150 | if (run_thread_.joinable()) // If unload is called more than once, don't join a second time. |
157 | 151 | { | 151 | { |
158 | 152 | run_thread_.join(); | 152 | run_thread_.join(); |
159 | @@ -183,11 +183,9 @@ | |||
160 | 183 | { | 183 | { |
161 | 184 | case ScopeState::Stopped: | 184 | case ScopeState::Stopped: |
162 | 185 | { | 185 | { |
168 | 186 | { | 186 | // cppcheck-suppress unreadVariable |
169 | 187 | // cppcheck-suppress unreadVariable | 187 | lock_guard<mutex> lock(cmd_mutex_); |
170 | 188 | lock_guard<mutex> lock(cmd_mutex_); | 188 | cmd_ = ScopeCmd::Start; |
166 | 189 | cmd_ = ScopeCmd::Start; | ||
167 | 190 | } | ||
171 | 191 | cmd_changed_.notify_all(); | 189 | cmd_changed_.notify_all(); |
172 | 192 | break; | 190 | break; |
173 | 193 | } | 191 | } |
174 | @@ -225,12 +223,11 @@ | |||
175 | 225 | { | 223 | { |
176 | 226 | case ScopeState::Started: | 224 | case ScopeState::Started: |
177 | 227 | { | 225 | { |
183 | 228 | { | 226 | // cppcheck-suppress unreadVariable |
184 | 229 | // cppcheck-suppress unreadVariable | 227 | lock_guard<mutex> lock(cmd_mutex_); |
185 | 230 | lock_guard<mutex> lock(cmd_mutex_); | 228 | cmd_ = ScopeCmd::Stop; |
181 | 231 | cmd_ = ScopeCmd::Stop; | ||
182 | 232 | } | ||
186 | 233 | cmd_changed_.notify_all(); | 229 | cmd_changed_.notify_all(); |
187 | 230 | break; | ||
188 | 234 | } | 231 | } |
189 | 235 | case ScopeState::Stopped: | 232 | case ScopeState::Stopped: |
190 | 236 | case ScopeState::Finished: | 233 | case ScopeState::Finished: |
191 | @@ -286,26 +283,22 @@ | |||
192 | 286 | unity::util::ResourcePtr<ScopeBase*, decltype(destroy_func)> scope_base(create_func(), destroy_func); | 283 | unity::util::ResourcePtr<ScopeBase*, decltype(destroy_func)> scope_base(create_func(), destroy_func); |
193 | 287 | if (scope_base.get() == nullptr) | 284 | if (scope_base.get() == nullptr) |
194 | 288 | { | 285 | { |
203 | 289 | { | 286 | // cppcheck-suppress unreadVariable |
204 | 290 | // cppcheck-suppress unreadVariable | 287 | unique_lock<mutex> lock(state_mutex_); |
205 | 291 | unique_lock<mutex> lock(state_mutex_); | 288 | scope_state_ = ScopeState::Failed; |
206 | 292 | scope_state_ = ScopeState::Failed; | 289 | exception_ = make_exception_ptr( |
207 | 293 | exception_ = make_exception_ptr( | 290 | unity::ResourceException("Scope " + scope_name_ + |
208 | 294 | unity::ResourceException("Scope " + scope_name_ + | 291 | " returned nullptr from " + UNITY_API_SCOPE_CREATE_SYMSTR)); |
201 | 295 | " returned nullptr from " + UNITY_API_SCOPE_CREATE_SYMSTR)); | ||
202 | 296 | } | ||
209 | 297 | state_changed_.notify_all(); | 292 | state_changed_.notify_all(); |
210 | 298 | return; | 293 | return; |
211 | 299 | } | 294 | } |
212 | 300 | 295 | ||
213 | 301 | // Notify the parent thread that we are ready to go. | 296 | // Notify the parent thread that we are ready to go. |
214 | 302 | { | 297 | { |
221 | 303 | { | 298 | // cppcheck-suppress unreadVariable |
222 | 304 | // cppcheck-suppress unreadVariable | 299 | unique_lock<mutex> lock(state_mutex_); |
223 | 305 | unique_lock<mutex> lock(state_mutex_); | 300 | scope_state_ = ScopeState::Stopped; |
224 | 306 | scope_state_ = ScopeState::Stopped; | 301 | scope_base_ = scope_base.get(); |
219 | 307 | scope_base_ = scope_base.get(); | ||
220 | 308 | } | ||
225 | 309 | state_changed_.notify_all(); | 302 | state_changed_.notify_all(); |
226 | 310 | } | 303 | } |
227 | 311 | 304 | ||
228 | @@ -397,7 +390,6 @@ | |||
229 | 397 | unity::ResourceException("scope " + scope_name_ + ": unknown exception in stop()")); | 390 | unity::ResourceException("scope " + scope_name_ + ": unknown exception in stop()")); |
230 | 398 | throw; | 391 | throw; |
231 | 399 | } | 392 | } |
232 | 400 | lock.unlock(); | ||
233 | 401 | if (run_thread_.joinable()) | 393 | if (run_thread_.joinable()) |
234 | 402 | { | 394 | { |
235 | 403 | run_thread_.join(); | 395 | run_thread_.join(); |
236 | @@ -436,11 +428,9 @@ | |||
237 | 436 | 428 | ||
238 | 437 | void ScopeLoader::handle_thread_exception() | 429 | void ScopeLoader::handle_thread_exception() |
239 | 438 | { | 430 | { |
245 | 439 | { | 431 | // cppcheck-suppress unreadVariable |
246 | 440 | // cppcheck-suppress unreadVariable | 432 | lock_guard<mutex> lock(state_mutex_); |
247 | 441 | lock_guard<mutex> lock(state_mutex_); | 433 | scope_state_ = ScopeState::Failed; |
243 | 442 | scope_state_ = ScopeState::Failed; | ||
244 | 443 | } | ||
248 | 444 | state_changed_.notify_all(); | 434 | state_changed_.notify_all(); |
249 | 445 | } | 435 | } |
250 | 446 | 436 | ||
251 | @@ -465,24 +455,20 @@ | |||
252 | 465 | exception_ = make_exception_ptr( | 455 | exception_ = make_exception_ptr( |
253 | 466 | unity::ResourceException("Scope " + scope_name_ + ": exception in run()")); | 456 | unity::ResourceException("Scope " + scope_name_ + ": exception in run()")); |
254 | 467 | scope_state_ = ScopeState::Failed; | 457 | scope_state_ = ScopeState::Failed; |
255 | 458 | state_changed_.notify_all(); | ||
256 | 468 | } | 459 | } |
257 | 469 | state_changed_.notify_all(); | ||
258 | 470 | 460 | ||
264 | 471 | { | 461 | // cppcheck-suppress unreadVariable |
265 | 472 | // cppcheck-suppress unreadVariable | 462 | lock_guard<mutex> lock(cmd_mutex_); |
266 | 473 | lock_guard<mutex> lock(cmd_mutex_); | 463 | cmd_ = ScopeCmd::Stop; |
262 | 474 | cmd_ = ScopeCmd::Stop; | ||
263 | 475 | } | ||
267 | 476 | cmd_changed_.notify_all(); | 464 | cmd_changed_.notify_all(); |
268 | 477 | } | 465 | } |
269 | 478 | } | 466 | } |
270 | 479 | 467 | ||
271 | 480 | void ScopeLoader::notify_app_thread_started() | 468 | void ScopeLoader::notify_app_thread_started() |
272 | 481 | { | 469 | { |
277 | 482 | { | 470 | unique_lock<mutex> lock(run_thread_mutex_); |
278 | 483 | unique_lock<mutex> lock(run_thread_mutex_); | 471 | run_thread_state_ = AppState::Started; |
275 | 484 | run_thread_state_ = AppState::Started; | ||
276 | 485 | } | ||
279 | 486 | run_thread_changed_.notify_all(); | 472 | run_thread_changed_.notify_all(); |
280 | 487 | } | 473 | } |
281 | 488 | 474 | ||
282 | 489 | 475 | ||
283 | === modified file 'src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp' | |||
284 | --- src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp 2013-10-16 15:04:14 +0000 | |||
285 | +++ src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp 2013-10-25 04:17:24 +0000 | |||
286 | @@ -152,7 +152,6 @@ | |||
287 | 152 | { | 152 | { |
288 | 153 | run_workers(); | 153 | run_workers(); |
289 | 154 | state_ = Active; | 154 | state_ = Active; |
290 | 155 | lock.unlock(); | ||
291 | 156 | state_changed_.notify_all(); | 155 | state_changed_.notify_all(); |
292 | 157 | break; | 156 | break; |
293 | 158 | } | 157 | } |
294 | @@ -200,7 +199,6 @@ | |||
295 | 200 | ServantMap().swap(servants_); // Not need for a try block. The ServantBase destructor is noexcept. | 199 | ServantMap().swap(servants_); // Not need for a try block. The ServantBase destructor is noexcept. |
296 | 201 | lock.lock(); | 200 | lock.lock(); |
297 | 202 | state_ = Inactive; | 201 | state_ = Inactive; |
298 | 203 | lock.unlock(); | ||
299 | 204 | state_changed_.notify_all(); | 202 | state_changed_.notify_all(); |
300 | 205 | break; | 203 | break; |
301 | 206 | } | 204 | } |
302 | 207 | 205 | ||
303 | === modified file 'src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp' | |||
304 | --- src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp 2013-10-16 15:04:14 +0000 | |||
305 | +++ src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp 2013-10-25 04:17:24 +0000 | |||
306 | @@ -109,7 +109,6 @@ | |||
307 | 109 | // TODO: get pool size from config | 109 | // TODO: get pool size from config |
308 | 110 | invokers_.reset(new ThreadPool(1)); | 110 | invokers_.reset(new ThreadPool(1)); |
309 | 111 | state_ = Started; | 111 | state_ = Started; |
310 | 112 | lock.unlock(); | ||
311 | 113 | state_changed_.notify_all(); | 112 | state_changed_.notify_all(); |
312 | 114 | break; | 113 | break; |
313 | 115 | } | 114 | } |
314 | @@ -149,7 +148,6 @@ | |||
315 | 149 | pair.second->wait_for_shutdown(); | 148 | pair.second->wait_for_shutdown(); |
316 | 150 | } | 149 | } |
317 | 151 | state_ = Stopped; | 150 | state_ = Stopped; |
318 | 152 | lock.unlock(); | ||
319 | 153 | state_changed_.notify_all(); | 151 | state_changed_.notify_all(); |
320 | 154 | break; | 152 | break; |
321 | 155 | } | 153 | } |
I believe we should always attempt to have mutexes lock and unlock at their most efficient points in code (Its one of those optimisations you can foresee while coding). I see RAII mutexes as great safety nets when making a mistake, or when a scope is exited unexpectedly. My point is, perhaps we should not remove those lock.unlock() lines completely, but instead move them a line below the notify. This not only gives the thread a few more clock ticks now, but later allows for code to be added to these methods without causing the mutex to be held any longer than it needs to.