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/internal/tracing.h" 26 #include "absl/base/thread_annotations.h" 27 #include "absl/synchronization/mutex.h" 28 29 namespace absl { 30 ABSL_NAMESPACE_BEGIN 31 32 // BlockingCounter 33 // 34 // This class allows a thread to block for a pre-specified number of actions. 35 // `BlockingCounter` maintains a single non-negative abstract integer "count" 36 // with an initial value `initial_count`. A thread can then call `Wait()` on 37 // this blocking counter to block until the specified number of events occur; 38 // worker threads then call 'DecrementCount()` on the counter upon completion of 39 // their work. Once the counter's internal "count" reaches zero, the blocked 40 // thread unblocks. 41 // 42 // A `BlockingCounter` requires the following: 43 // - its `initial_count` is non-negative. 44 // - the number of calls to `DecrementCount()` on it is at most 45 // `initial_count`. 46 // - `Wait()` is called at most once on it. 47 // 48 // Given the above requirements, a `BlockingCounter` provides the following 49 // guarantees: 50 // - Once its internal "count" reaches zero, no legal action on the object 51 // can further change the value of "count". 52 // - When `Wait()` returns, it is legal to destroy the `BlockingCounter`. 53 // - When `Wait()` returns, the number of calls to `DecrementCount()` on 54 // this blocking counter exactly equals `initial_count`. 55 // 56 // Example: 57 // BlockingCounter bcount(N); // there are N items of work 58 // ... Allow worker threads to start. 59 // ... On completing each work item, workers do: 60 // ... bcount.DecrementCount(); // an item of work has been completed 61 // 62 // bcount.Wait(); // wait for all work to be complete 63 // 64 class BlockingCounter { 65 public: 66 explicit BlockingCounter(int initial_count); 67 68 BlockingCounter(const BlockingCounter&) = delete; 69 BlockingCounter& operator=(const BlockingCounter&) = delete; 70 71 // BlockingCounter::DecrementCount() 72 // 73 // Decrements the counter's "count" by one, and return "count == 0". This 74 // function requires that "count != 0" when it is called. 75 // 76 // Memory ordering: For any threads X and Y, any action taken by X 77 // before it calls `DecrementCount()` is visible to thread Y after 78 // Y's call to `DecrementCount()`, provided Y's call returns `true`. 79 bool DecrementCount(); 80 81 // BlockingCounter::Wait() 82 // 83 // Blocks until the counter reaches zero. This function may be called at most 84 // once. On return, `DecrementCount()` will have been called "initial_count" 85 // times and the blocking counter may be destroyed. 86 // 87 // Memory ordering: For any threads X and Y, any action taken by X 88 // before X calls `DecrementCount()` is visible to Y after Y returns 89 // from `Wait()`. 90 void Wait(); 91 92 private: 93 // Convenience helper to reduce verbosity at call sites. TraceObjectKind()94 static inline constexpr base_internal::ObjectKind TraceObjectKind() { 95 return base_internal::ObjectKind::kBlockingCounter; 96 } 97 98 Mutex lock_; 99 std::atomic<int> count_; 100 int num_waiting_ ABSL_GUARDED_BY(lock_); 101 bool done_ ABSL_GUARDED_BY(lock_); 102 }; 103 104 ABSL_NAMESPACE_END 105 } // namespace absl 106 107 #endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ 108