@@ -133,48 +133,56 @@ template <class Item> class BackgroundProducer {
133133 virtual bool Produce (Item &) = 0;
134134
135135 private:
136- vector<Item> ring_;
136+ // ~constant:
137137 int R_;
138138 unique_ptr<thread> worker_;
139+
140+ // writable by either thread:
139141 atomic<bool > stop_;
142+
143+ // writable by background producer thread:
144+ vector<Item> ring_;
145+ atomic<long long > p_; // # produced
140146 string errmsg_;
141- atomic<long long > p_, // produced
142- c_; // if c_>0 then item (c_-1)%R is currently being consumed
143- chrono::duration<double > p_blocked_ = chrono::duration<double >::zero(),
144- c_blocked_ = chrono::duration<double >::zero();
145147 chrono::time_point<chrono::high_resolution_clock> t0_;
148+ chrono::duration<double > p_blocked_ = chrono::duration<double >::zero();
146149
147- bool empty () { return p_ == c_; }
148-
149- bool full () { return p_ - max (c_.load (), 1LL ) == R_ - 1 ; }
150+ // writable by consumer thread:
151+ atomic<long long > c_; // if c_>0 then item (c_-1)%R is currently being consumed
152+ const Item *item_ = nullptr ;
153+ chrono::duration<double > c_blocked_ = chrono::duration<double >::zero();
150154
151155 void background_thread () {
152156 t0_ = chrono::high_resolution_clock::now ();
153157 do {
154- assert (p_ >= c_ );
158+ auto p = p_. load (memory_order_acquire );
155159 bool ok = false ;
156160 try {
157- ok = Produce (ring_[p_ % R_]);
161+ ok = Produce (ring_[p % R_]);
158162 } catch (exception &exn) {
159163 errmsg_ = exn.what ();
160164 if (errmsg_.empty ()) {
161165 errmsg_ = " unknown error on producer thread" ;
162166 }
163- stop_ = true ;
167+ stop_. store ( true , memory_order_release) ;
164168 }
165169 if (ok) {
166- ++p_;
167- if (full ()) {
170+ ++p;
171+ p_.store (p, memory_order_release);
172+ if (p - max (c_.load (memory_order_acquire), 1LL ) == R_ - 1 ) {
168173 auto t_spin = chrono::high_resolution_clock::now ();
169174 do {
175+ // assumption -- producer will usually be faster than the consumer, and
176+ // ringsize_ provides buffer if that's occasionally not the case
170177 this_thread::sleep_for (chrono::milliseconds (1 ));
171- } while (!stop_ && full ());
178+ } while (!stop_.load (memory_order_relaxed) &&
179+ (p - max (c_.load (memory_order_acquire), 1LL ) == R_ - 1 ));
172180 p_blocked_ += chrono::high_resolution_clock::now () - t_spin;
173181 }
174182 } else {
175- stop_ = true ;
183+ stop_. store ( true , memory_order_relaxed) ;
176184 }
177- } while (!stop_);
185+ } while (!stop_. load (memory_order_relaxed) );
178186 }
179187
180188 public:
@@ -192,36 +200,36 @@ template <class Item> class BackgroundProducer {
192200 bool next () {
193201 if (!worker_) {
194202 while (ring_.size () < R_) {
195- ring_.push_back ( Item () );
203+ ring_.emplace_back ( );
196204 }
197205 worker_.reset (new thread ([this ]() { this ->background_thread (); }));
198206 }
199- if (empty ()) {
200- auto t_start = chrono::high_resolution_clock::now ();
201- while (!stop_ && empty ()) {
207+ auto c = c_.load (memory_order_acquire), p = p_.load (memory_order_acquire);
208+ if (c == p) {
209+ auto t_spin = chrono::high_resolution_clock::now ();
210+ while (c == p && !stop_.load (memory_order_relaxed)) {
202211 this_thread::yield ();
212+ p = p_.load (memory_order_acquire);
203213 }
204- c_blocked_ += chrono::high_resolution_clock::now () - t_start ;
214+ c_blocked_ += chrono::high_resolution_clock::now () - t_spin ;
205215 }
206- if (stop_ && empty ( )) {
216+ if (c == p && stop_. load (memory_order_acquire )) {
207217 if (!errmsg_.empty ()) {
208218 throw runtime_error (errmsg_);
209219 }
210220 return false ;
211221 }
212- assert (c_ < p_);
213- c_++;
222+ assert (c < p);
223+ item_ = &ring_[c % R_];
224+ c_.store (c + 1 , memory_order_release);
214225 return true ;
215226 }
216227
217228 // get current item for consumption; defined only after next() returned true
218- Item &item () {
219- assert (c_ && errmsg_.empty ());
220- return ring_[(c_ - 1 ) % R_];
221- }
229+ const Item &item () { return *item_; }
222230
223231 void abort () {
224- stop_ = true ;
232+ stop_. store ( true , memory_order_relaxed) ;
225233 if (worker_) {
226234 worker_->join ();
227235 }
0 commit comments