Merge lp:~michihenning/unity-scopes-api/signal-before-unlock into lp:unity-scopes-api

Proposed by Michi Henning
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
Reviewer Review Type Date Requested Status
Marcus Tomlinson (community) Approve
Review via email: mp+192634@code.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

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.

review: Needs Information
Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
Marcus Tomlinson (marcustomlinson) :
review: Approve
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'demo/client.cpp'
--- demo/client.cpp 2013-09-23 06:13:06 +0000
+++ demo/client.cpp 2013-10-25 04:17:24 +0000
@@ -41,10 +41,8 @@
41 virtual void finished() override41 virtual void finished() override
42 {42 {
43 cout << "query complete" << endl;43 cout << "query complete" << endl;
44 {44 unique_lock<decltype(mutex_)> lock(mutex_);
45 unique_lock<decltype(mutex_)> lock(mutex_);45 query_complete_ = true;
46 query_complete_ = true;
47 }
48 condvar_.notify_one();46 condvar_.notify_one();
49 }47 }
5048
5149
=== modified file 'demo/scope-C.cpp'
--- demo/scope-C.cpp 2013-08-28 06:26:46 +0000
+++ demo/scope-C.cpp 2013-10-25 04:17:24 +0000
@@ -44,10 +44,8 @@
44public:44public:
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)
46 {46 {
47 {47 std::lock_guard<std::mutex> lock(mutex_);
48 std::lock_guard<std::mutex> lock(mutex_);48 queries_.push_back(QueryData { query, query_string, reply_proxy });
49 queries_.push_back(QueryData { query, query_string, reply_proxy });
50 }
51 condvar_.notify_one();49 condvar_.notify_one();
52 }50 }
5351
@@ -57,7 +55,6 @@
57 condvar_.wait(lock, [this] { return !queries_.empty() || done_; });55 condvar_.wait(lock, [this] { return !queries_.empty() || done_; });
58 if (done_)56 if (done_)
59 {57 {
60 lock.unlock();
61 condvar_.notify_all();58 condvar_.notify_all();
62 }59 }
63 else60 else
@@ -88,11 +85,9 @@
8885
89 void finish()86 void finish()
90 {87 {
91 {88 std::unique_lock<std::mutex> lock(mutex_);
92 std::unique_lock<std::mutex> lock(mutex_);89 queries_.clear();
93 queries_.clear();90 done_ = true;
94 done_ = true;
95 }
96 condvar_.notify_all();91 condvar_.notify_all();
97 }92 }
9893
9994
=== modified file 'demo/scope-D.cpp'
--- demo/scope-D.cpp 2013-08-28 06:26:46 +0000
+++ demo/scope-D.cpp 2013-10-25 04:17:24 +0000
@@ -47,10 +47,8 @@
47public:47public:
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)
49 {49 {
50 {50 std::lock_guard<std::mutex> lock(mutex_);
51 std::lock_guard<std::mutex> lock(mutex_);51 queries_.push_back(QueryData { query, query_string, reply_proxy });
52 queries_.push_back(QueryData { query, query_string, reply_proxy });
53 }
54 condvar_.notify_one();52 condvar_.notify_one();
55 }53 }
5654
@@ -60,7 +58,6 @@
60 condvar_.wait(lock, [this] { return !queries_.empty() || done_; });58 condvar_.wait(lock, [this] { return !queries_.empty() || done_; });
61 if (done_)59 if (done_)
62 {60 {
63 lock.unlock();
64 condvar_.notify_all();61 condvar_.notify_all();
65 }62 }
66 else63 else
@@ -91,11 +88,9 @@
9188
92 void finish()89 void finish()
93 {90 {
94 {91 std::unique_lock<std::mutex> lock(mutex_);
95 std::unique_lock<std::mutex> lock(mutex_);92 queries_.clear();
96 queries_.clear();93 done_ = true;
97 done_ = true;
98 }
99 condvar_.notify_all();94 condvar_.notify_all();
100 }95 }
10196
10297
=== modified file 'src/unity/api/scopes/internal/Reaper.cpp'
--- src/unity/api/scopes/internal/Reaper.cpp 2013-08-27 06:28:14 +0000
+++ src/unity/api/scopes/internal/Reaper.cpp 2013-10-25 04:17:24 +0000
@@ -126,8 +126,8 @@
126 {126 {
127 lock_guard<mutex> lock(mutex_);127 lock_guard<mutex> lock(mutex_);
128 finish_ = true;128 finish_ = true;
129 do_work_.notify_one();
129 }130 }
130 do_work_.notify_one();
131 reap_thread_.join();131 reap_thread_.join();
132}132}
133133
@@ -161,17 +161,15 @@
161161
162 // Put new Item at the head of the list.162 // Put new Item at the head of the list.
163 reaper_private::Reaplist::iterator ri;163 reaper_private::Reaplist::iterator ri;
164 size_t list_size;
165 {164 {
166 lock_guard<mutex> lock(mutex_);165 lock_guard<mutex> lock(mutex_);
167 Item item(cb);166 Item item(cb);
168 list_.push_front(item); // LRU order167 list_.push_front(item); // LRU order
169 ri = list_.begin();168 ri = list_.begin();
170 list_size = list_.size();169 if (list_.size() == 1) // List just became non-empty
171 }170 {
172 if (list_size == 1) // List just became non-empty171 do_work_.notify_one(); // Wake up reaper thread
173 {172 }
174 do_work_.notify_one(); // Wake up reaper thread
175 }173 }
176174
177 // Make a new ReapItem.175 // Make a new ReapItem.
178176
=== modified file 'src/unity/api/scopes/internal/ReplyObject.cpp'
--- src/unity/api/scopes/internal/ReplyObject.cpp 2013-08-27 06:28:14 +0000
+++ src/unity/api/scopes/internal/ReplyObject.cpp 2013-10-25 04:17:24 +0000
@@ -117,7 +117,6 @@
117 lock.lock();117 lock.lock();
118 if (--num_push_ == 0)118 if (--num_push_ == 0)
119 {119 {
120 lock.unlock();
121 idle_.notify_one();120 idle_.notify_one();
122 }121 }
123}122}
124123
=== modified file 'src/unity/api/scopes/internal/ScopeLoader.cpp'
--- src/unity/api/scopes/internal/ScopeLoader.cpp 2013-08-20 05:25:28 +0000
+++ src/unity/api/scopes/internal/ScopeLoader.cpp 2013-10-25 04:17:24 +0000
@@ -145,8 +145,8 @@
145 // FALLTHROUGH145 // FALLTHROUGH
146 case ScopeState::Finished:146 case ScopeState::Finished:
147 {147 {
148 cmd_changed_.notify_all();
148 lock.unlock();149 lock.unlock();
149 cmd_changed_.notify_all();
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.
151 {151 {
152 run_thread_.join();152 run_thread_.join();
@@ -183,11 +183,9 @@
183 {183 {
184 case ScopeState::Stopped:184 case ScopeState::Stopped:
185 {185 {
186 {186 // cppcheck-suppress unreadVariable
187 // cppcheck-suppress unreadVariable187 lock_guard<mutex> lock(cmd_mutex_);
188 lock_guard<mutex> lock(cmd_mutex_);188 cmd_ = ScopeCmd::Start;
189 cmd_ = ScopeCmd::Start;
190 }
191 cmd_changed_.notify_all();189 cmd_changed_.notify_all();
192 break;190 break;
193 }191 }
@@ -225,12 +223,11 @@
225 {223 {
226 case ScopeState::Started:224 case ScopeState::Started:
227 {225 {
228 {226 // cppcheck-suppress unreadVariable
229 // cppcheck-suppress unreadVariable227 lock_guard<mutex> lock(cmd_mutex_);
230 lock_guard<mutex> lock(cmd_mutex_);228 cmd_ = ScopeCmd::Stop;
231 cmd_ = ScopeCmd::Stop;
232 }
233 cmd_changed_.notify_all();229 cmd_changed_.notify_all();
230 break;
234 }231 }
235 case ScopeState::Stopped:232 case ScopeState::Stopped:
236 case ScopeState::Finished:233 case ScopeState::Finished:
@@ -286,26 +283,22 @@
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);
287 if (scope_base.get() == nullptr)284 if (scope_base.get() == nullptr)
288 {285 {
289 {286 // cppcheck-suppress unreadVariable
290 // cppcheck-suppress unreadVariable287 unique_lock<mutex> lock(state_mutex_);
291 unique_lock<mutex> lock(state_mutex_);288 scope_state_ = ScopeState::Failed;
292 scope_state_ = ScopeState::Failed;289 exception_ = make_exception_ptr(
293 exception_ = make_exception_ptr(290 unity::ResourceException("Scope " + scope_name_ +
294 unity::ResourceException("Scope " + scope_name_ +291 " returned nullptr from " + UNITY_API_SCOPE_CREATE_SYMSTR));
295 " returned nullptr from " + UNITY_API_SCOPE_CREATE_SYMSTR));
296 }
297 state_changed_.notify_all();292 state_changed_.notify_all();
298 return;293 return;
299 }294 }
300295
301 // Notify the parent thread that we are ready to go.296 // Notify the parent thread that we are ready to go.
302 {297 {
303 {298 // cppcheck-suppress unreadVariable
304 // cppcheck-suppress unreadVariable299 unique_lock<mutex> lock(state_mutex_);
305 unique_lock<mutex> lock(state_mutex_);300 scope_state_ = ScopeState::Stopped;
306 scope_state_ = ScopeState::Stopped;301 scope_base_ = scope_base.get();
307 scope_base_ = scope_base.get();
308 }
309 state_changed_.notify_all();302 state_changed_.notify_all();
310 }303 }
311304
@@ -397,7 +390,6 @@
397 unity::ResourceException("scope " + scope_name_ + ": unknown exception in stop()"));390 unity::ResourceException("scope " + scope_name_ + ": unknown exception in stop()"));
398 throw;391 throw;
399 }392 }
400 lock.unlock();
401 if (run_thread_.joinable())393 if (run_thread_.joinable())
402 {394 {
403 run_thread_.join();395 run_thread_.join();
@@ -436,11 +428,9 @@
436428
437void ScopeLoader::handle_thread_exception()429void ScopeLoader::handle_thread_exception()
438{430{
439 {431 // cppcheck-suppress unreadVariable
440 // cppcheck-suppress unreadVariable432 lock_guard<mutex> lock(state_mutex_);
441 lock_guard<mutex> lock(state_mutex_);433 scope_state_ = ScopeState::Failed;
442 scope_state_ = ScopeState::Failed;
443 }
444 state_changed_.notify_all();434 state_changed_.notify_all();
445}435}
446436
@@ -465,24 +455,20 @@
465 exception_ = make_exception_ptr(455 exception_ = make_exception_ptr(
466 unity::ResourceException("Scope " + scope_name_ + ": exception in run()"));456 unity::ResourceException("Scope " + scope_name_ + ": exception in run()"));
467 scope_state_ = ScopeState::Failed;457 scope_state_ = ScopeState::Failed;
458 state_changed_.notify_all();
468 }459 }
469 state_changed_.notify_all();
470460
471 {461 // cppcheck-suppress unreadVariable
472 // cppcheck-suppress unreadVariable462 lock_guard<mutex> lock(cmd_mutex_);
473 lock_guard<mutex> lock(cmd_mutex_);463 cmd_ = ScopeCmd::Stop;
474 cmd_ = ScopeCmd::Stop;
475 }
476 cmd_changed_.notify_all();464 cmd_changed_.notify_all();
477 }465 }
478}466}
479467
480void ScopeLoader::notify_app_thread_started()468void ScopeLoader::notify_app_thread_started()
481{469{
482 {470 unique_lock<mutex> lock(run_thread_mutex_);
483 unique_lock<mutex> lock(run_thread_mutex_);471 run_thread_state_ = AppState::Started;
484 run_thread_state_ = AppState::Started;
485 }
486 run_thread_changed_.notify_all();472 run_thread_changed_.notify_all();
487}473}
488474
489475
=== modified file 'src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp'
--- src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp 2013-10-16 15:04:14 +0000
+++ src/unity/api/scopes/internal/zmq_middleware/ObjectAdapter.cpp 2013-10-25 04:17:24 +0000
@@ -152,7 +152,6 @@
152 {152 {
153 run_workers();153 run_workers();
154 state_ = Active;154 state_ = Active;
155 lock.unlock();
156 state_changed_.notify_all();155 state_changed_.notify_all();
157 break;156 break;
158 }157 }
@@ -200,7 +199,6 @@
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.
201 lock.lock();200 lock.lock();
202 state_ = Inactive;201 state_ = Inactive;
203 lock.unlock();
204 state_changed_.notify_all();202 state_changed_.notify_all();
205 break;203 break;
206 }204 }
207205
=== modified file 'src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp'
--- src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp 2013-10-16 15:04:14 +0000
+++ src/unity/api/scopes/internal/zmq_middleware/ZmqMiddleware.cpp 2013-10-25 04:17:24 +0000
@@ -109,7 +109,6 @@
109 // TODO: get pool size from config109 // TODO: get pool size from config
110 invokers_.reset(new ThreadPool(1));110 invokers_.reset(new ThreadPool(1));
111 state_ = Started;111 state_ = Started;
112 lock.unlock();
113 state_changed_.notify_all();112 state_changed_.notify_all();
114 break;113 break;
115 }114 }
@@ -149,7 +148,6 @@
149 pair.second->wait_for_shutdown();148 pair.second->wait_for_shutdown();
150 }149 }
151 state_ = Stopped;150 state_ = Stopped;
152 lock.unlock();
153 state_changed_.notify_all();151 state_changed_.notify_all();
154 break;152 break;
155 }153 }

Subscribers

People subscribed via source and target branches

to all changes: