Merge lp:~michihenning/unity-scopes-api/reaper-hang into lp:unity-scopes-api

Proposed by Michi Henning
Status: Merged
Approved by: Jussi Pakkanen
Approved revision: 94
Merged at revision: 111
Proposed branch: lp:~michihenning/unity-scopes-api/reaper-hang
Merge into: lp:unity-scopes-api
Diff against target: 150 lines (+43/-23)
2 files modified
include/scopes/internal/Reaper.h (+6/-1)
src/internal/Reaper.cpp (+37/-22)
To merge this branch: bzr merge lp:~michihenning/unity-scopes-api/reaper-hang
Reviewer Review Type Date Requested Status
Jussi Pakkanen (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+199209@code.launchpad.net

Commit message

Got rid of chatter from helgrind. (Needs helgrind 3.9.0 or later--3.8.1 generates bogus errors.)
Added public destroy() method so it is possible to shut down the reaper explicitly.
Fixed race condition on setting the self_ weak_ptr.

Description of the change

Got rid of chatter from helgrind. (Needs helgrind 3.9.0 or later--3.8.1 generates bogus errors.)
Added public destroy() method so it is possible to shut down the reaper explicitly.
Fixed race condition on setting the self_ weak_ptr.

To post a comment you must log in.
Revision history for this message
Michi Henning (michihenning) wrote :

OK, I think I found it. The problem was that add() could run before self_ was set, so the back pointer form a reap item to the reaper was stale. Jussi, thanks again for the stack traces! Without those, I wouldn't have spotted this.

Can you give this a spin please and see whether it works for you now?

While I was at it, I got rid of the chatter from helgrind and added a public destroy() method as a convenience function.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

Let's approve this because it's good even though it will not fix the bug entirely.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/scopes/internal/Reaper.h'
2--- include/scopes/internal/Reaper.h 2013-11-21 21:44:00 +0000
3+++ include/scopes/internal/Reaper.h 2013-12-17 00:55:11 +0000
4@@ -137,6 +137,10 @@
5 // the total number of items).
6 static SPtr create(int reap_interval, int expiry_interval, DestroyPolicy p = NoCallbackOnDestroy);
7
8+ // Destroys the reaper and returns once any remaining items have been reaped (depending on the
9+ // destroy policy). The destructor implicitly calls destroy().
10+ void destroy();
11+
12 // Adds a new item to the reaper. The reaper calls cb once the item has not been refreshed for at
13 // least expiry_interval seconds. O(1) performance.
14 // The callback passed to add() must not block for any length of time. We have only one reaper
15@@ -152,8 +156,9 @@
16 private:
17 Reaper(int reap_interval, int expiry_interval, DestroyPolicy p);
18 void set_self() noexcept;
19+ void start();
20
21- void run(); // Start function for reaper thread
22+ void reap_func(); // Start function for reaper thread
23
24 void remove_zombies(reaper_private::Reaplist const&) noexcept; // Invokes callbacks for expired entries
25
26
27=== modified file 'src/internal/Reaper.cpp'
28--- src/internal/Reaper.cpp 2013-11-25 16:22:12 +0000
29+++ src/internal/Reaper.cpp 2013-12-17 00:55:11 +0000
30@@ -71,7 +71,7 @@
31 auto const reaper = wp_reaper.lock();
32 if (reaper)
33 {
34- lock_guard<mutex> lock(reaper->mutex_);
35+ lock_guard<mutex> reaper_lock(reaper->mutex_);
36 assert(it_ != reaper->list_.end());
37 reaper_private::Item item(*it_);
38 item.timestamp = chrono::steady_clock::now();
39@@ -101,7 +101,7 @@
40 auto const reaper = wp_reaper.lock();
41 if (reaper)
42 {
43- lock_guard<mutex> lock(reaper->mutex_);
44+ lock_guard<mutex> reaper_lock(reaper->mutex_);
45 assert(it_ != reaper->list_.end());
46 reaper->list_.erase(it_);
47 }
48@@ -129,18 +129,11 @@
49 s << "Reaper: reap_interval (" << reap_interval << ") must be <= expiry_interval (" << expiry_interval << ").";
50 throw unity::LogicException(s.str());
51 }
52- reap_thread_ = thread(&Reaper::run, this);
53 }
54
55 Reaper::~Reaper() noexcept
56 {
57- // Let the reaper thread know that it needs to stop doing things
58- {
59- lock_guard<mutex> lock(mutex_);
60- finish_ = true;
61- }
62- do_work_.notify_one();
63- reap_thread_.join();
64+ destroy();
65 }
66
67 // Instantiate a new reaper. We call set_self() after instantiation so the reaper
68@@ -152,6 +145,7 @@
69 {
70 SPtr reaper(new Reaper(reap_interval, expiry_interval, p));
71 reaper->set_self();
72+ reaper->start();
73 return reaper;
74 }
75
76@@ -160,6 +154,26 @@
77 self_ = shared_from_this();
78 }
79
80+void Reaper::start()
81+{
82+ reap_thread_ = thread(&Reaper::reap_func, this);
83+}
84+
85+void Reaper::destroy()
86+{
87+ // Let the reaper thread know that it needs to stop doing things
88+ {
89+ lock_guard<mutex> lock(mutex_);
90+ if (finish_)
91+ {
92+ return;
93+ }
94+ finish_ = true;
95+ do_work_.notify_one();
96+ }
97+ reap_thread_.join();
98+}
99+
100 // Add a new entry to the reaper. If the entry is not refreshed within the expiry interval,
101 // the reaper removes the item from the list and calls cb to let the caller know about the expiry.
102
103@@ -170,17 +184,19 @@
104 throw unity::InvalidArgumentException("Reaper: invalid null callback passed to add().");
105 }
106
107+ lock_guard<mutex> lock(mutex_);
108+
109+ if (finish_)
110+ {
111+ throw unity::LogicException("Reaper: cannot add item to destroyed reaper.");
112+ }
113+
114 // Put new Item at the head of the list.
115 reaper_private::Reaplist::iterator ri;
116- size_t list_size;
117- {
118- lock_guard<mutex> lock(mutex_);
119- Item item(cb);
120- list_.push_front(item); // LRU order
121- ri = list_.begin();
122- list_size = list_.size();
123- }
124- if (list_size == 1) // List just became non-empty
125+ Item item(cb);
126+ list_.push_front(item); // LRU order
127+ ri = list_.begin();
128+ if (list_.size() == 1)
129 {
130 do_work_.notify_one(); // Wake up reaper thread
131 }
132@@ -201,7 +217,7 @@
133
134 // Reaper thread
135
136-void Reaper::run()
137+void Reaper::reap_func()
138 {
139 unique_lock<mutex> lock(mutex_);
140 for (;;)
141@@ -225,8 +241,7 @@
142 auto const reap_interval = chrono::duration_cast<chrono::milliseconds>(reap_interval_);
143 auto const sleep_interval = max(expiry_interval_ - oldest_item_age, reap_interval);
144 auto const wakeup_time = now + sleep_interval;
145- while (!finish_ && do_work_.wait_until(lock, wakeup_time) != cv_status::timeout)
146- ;
147+ do_work_.wait_until(lock, wakeup_time, [this]{ return finish_; });
148 }
149
150 if (finish_ && policy_ == NoCallbackOnDestroy)

Subscribers

People subscribed via source and target branches

to all changes: