1 // 2 // Copyright 2017 The Abseil Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 // ----------------------------------------------------------------------------- 17 // blocking_counter.h 18 // ----------------------------------------------------------------------------- 19 20 #ifndef ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ 21 #define ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ 22 23 #include <atomic> 24 25 #include "absl/base/thread_annotations.h" 26 #include "absl/synchronization/mutex.h" 27 28 namespace absl { 29 ABSL_NAMESPACE_BEGIN 30 31 // BlockingCounter 32 // 33 // This class allows a thread to block for a pre-specified number of actions. 34 // `BlockingCounter` maintains a single non-negative abstract integer "count" 35 // with an initial value `initial_count`. A thread can then call `Wait()` on 36 // this blocking counter to block until the specified number of events occur; 37 // worker threads then call 'DecrementCount()` on the counter upon completion of 38 // their work. Once the counter's internal "count" reaches zero, the blocked 39 // thread unblocks. 40 // 41 // A `BlockingCounter` requires the following: 42 // - its `initial_count` is non-negative. 43 // - the number of calls to `DecrementCount()` on it is at most 44 // `initial_count`. 45 // - `Wait()` is called at most once on it. 46 // 47 // Given the above requirements, a `BlockingCounter` provides the following 48 // guarantees: 49 // - Once its internal "count" reaches zero, no legal action on the object 50 // can further change the value of "count". 51 // - When `Wait()` returns, it is legal to destroy the `BlockingCounter`. 52 // - When `Wait()` returns, the number of calls to `DecrementCount()` on 53 // this blocking counter exactly equals `initial_count`. 54 // 55 // Example: 56 // BlockingCounter bcount(N); // there are N items of work 57 // ... Allow worker threads to start. 58 // ... On completing each work item, workers do: 59 // ... bcount.DecrementCount(); // an item of work has been completed 60 // 61 // bcount.Wait(); // wait for all work to be complete 62 // 63 class BlockingCounter { 64 public: 65 explicit BlockingCounter(int initial_count); 66 67 BlockingCounter(const BlockingCounter&) = delete; 68 BlockingCounter& operator=(const BlockingCounter&) = delete; 69 70 // BlockingCounter::DecrementCount() 71 // 72 // Decrements the counter's "count" by one, and return "count == 0". This 73 // function requires that "count != 0" when it is called. 74 // 75 // Memory ordering: For any threads X and Y, any action taken by X 76 // before it calls `DecrementCount()` is visible to thread Y after 77 // Y's call to `DecrementCount()`, provided Y's call returns `true`. 78 bool DecrementCount(); 79 80 // BlockingCounter::Wait() 81 // 82 // Blocks until the counter reaches zero. This function may be called at most 83 // once. On return, `DecrementCount()` will have been called "initial_count" 84 // times and the blocking counter may be destroyed. 85 // 86 // Memory ordering: For any threads X and Y, any action taken by X 87 // before X calls `DecrementCount()` is visible to Y after Y returns 88 // from `Wait()`. 89 void Wait(); 90 91 private: 92 Mutex lock_; 93 std::atomic<int> count_; 94 int num_waiting_ ABSL_GUARDED_BY(lock_); 95 bool done_ ABSL_GUARDED_BY(lock_); 96 }; 97 98 ABSL_NAMESPACE_END 99 } // namespace absl 100 101 #endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ 102