1 /* 2 * Copyright 2019 The libgav1 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 * http://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 #ifndef LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 18 #define LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 19 20 #include <cassert> 21 #include <condition_variable> // NOLINT (unapproved c++11 header) 22 #include <mutex> // NOLINT (unapproved c++11 header) 23 24 #include "src/utils/compiler_attributes.h" 25 26 namespace libgav1 { 27 28 // Implementation of a Blocking Counter that is used for the "fork-join" 29 // use case. Typical usage would be as follows: 30 // BlockingCounter counter(num_jobs); 31 // - spawn the jobs. 32 // - call counter.Wait() on the master thread. 33 // - worker threads will call counter.Decrement(). 34 // - master thread will return from counter.Wait() when all workers are 35 // complete. 36 template <bool has_failure_status> 37 class BlockingCounterImpl { 38 public: BlockingCounterImpl(int initial_count)39 explicit BlockingCounterImpl(int initial_count) 40 : count_(initial_count), job_failed_(false) {} 41 42 // Increment the counter by |count|. This must be called before Wait() is 43 // called. This must be called from the same thread that will call Wait(). IncrementBy(int count)44 void IncrementBy(int count) { 45 assert(count >= 0); 46 std::unique_lock<std::mutex> lock(mutex_); 47 count_ += count; 48 } 49 50 // Decrement the counter by 1. This function can be called only when 51 // |has_failure_status| is false (i.e.) when this class is being used with the 52 // |BlockingCounter| alias. Decrement()53 void Decrement() { 54 static_assert(!has_failure_status, ""); 55 std::unique_lock<std::mutex> lock(mutex_); 56 if (--count_ == 0) { 57 condition_.notify_one(); 58 } 59 } 60 61 // Decrement the counter by 1. This function can be called only when 62 // |has_failure_status| is true (i.e.) when this class is being used with the 63 // |BlockingCounterWithStatus| alias. |job_succeeded| is used to update the 64 // state of |job_failed_|. Decrement(bool job_succeeded)65 void Decrement(bool job_succeeded) { 66 static_assert(has_failure_status, ""); 67 std::unique_lock<std::mutex> lock(mutex_); 68 job_failed_ |= !job_succeeded; 69 if (--count_ == 0) { 70 condition_.notify_one(); 71 } 72 } 73 74 // Block until the counter becomes 0. This function can be called only once 75 // per object. If |has_failure_status| is true, true is returned if all the 76 // jobs succeeded and false is returned if any of the jobs failed. If 77 // |has_failure_status| is false, this function always returns true. Wait()78 bool Wait() { 79 std::unique_lock<std::mutex> lock(mutex_); 80 condition_.wait(lock, [this]() { return count_ == 0; }); 81 // If |has_failure_status| is false, we simply return true. 82 return has_failure_status ? !job_failed_ : true; 83 } 84 85 private: 86 std::mutex mutex_; 87 std::condition_variable condition_; 88 int count_ LIBGAV1_GUARDED_BY(mutex_); 89 bool job_failed_ LIBGAV1_GUARDED_BY(mutex_); 90 }; 91 92 using BlockingCounterWithStatus = BlockingCounterImpl<true>; 93 using BlockingCounter = BlockingCounterImpl<false>; 94 95 } // namespace libgav1 96 97 #endif // LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 98