1*09537850SAkhilesh Sanikop /* 2*09537850SAkhilesh Sanikop * Copyright 2019 The libgav1 Authors 3*09537850SAkhilesh Sanikop * 4*09537850SAkhilesh Sanikop * Licensed under the Apache License, Version 2.0 (the "License"); 5*09537850SAkhilesh Sanikop * you may not use this file except in compliance with the License. 6*09537850SAkhilesh Sanikop * You may obtain a copy of the License at 7*09537850SAkhilesh Sanikop * 8*09537850SAkhilesh Sanikop * http://www.apache.org/licenses/LICENSE-2.0 9*09537850SAkhilesh Sanikop * 10*09537850SAkhilesh Sanikop * Unless required by applicable law or agreed to in writing, software 11*09537850SAkhilesh Sanikop * distributed under the License is distributed on an "AS IS" BASIS, 12*09537850SAkhilesh Sanikop * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*09537850SAkhilesh Sanikop * See the License for the specific language governing permissions and 14*09537850SAkhilesh Sanikop * limitations under the License. 15*09537850SAkhilesh Sanikop */ 16*09537850SAkhilesh Sanikop 17*09537850SAkhilesh Sanikop #ifndef LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 18*09537850SAkhilesh Sanikop #define LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 19*09537850SAkhilesh Sanikop 20*09537850SAkhilesh Sanikop #include <cassert> 21*09537850SAkhilesh Sanikop #include <condition_variable> // NOLINT (unapproved c++11 header) 22*09537850SAkhilesh Sanikop #include <mutex> // NOLINT (unapproved c++11 header) 23*09537850SAkhilesh Sanikop 24*09537850SAkhilesh Sanikop #include "src/utils/compiler_attributes.h" 25*09537850SAkhilesh Sanikop 26*09537850SAkhilesh Sanikop namespace libgav1 { 27*09537850SAkhilesh Sanikop 28*09537850SAkhilesh Sanikop // Implementation of a Blocking Counter that is used for the "fork-join" 29*09537850SAkhilesh Sanikop // use case. Typical usage would be as follows: 30*09537850SAkhilesh Sanikop // BlockingCounter counter(num_jobs); 31*09537850SAkhilesh Sanikop // - spawn the jobs. 32*09537850SAkhilesh Sanikop // - call counter.Wait() on the master thread. 33*09537850SAkhilesh Sanikop // - worker threads will call counter.Decrement(). 34*09537850SAkhilesh Sanikop // - master thread will return from counter.Wait() when all workers are 35*09537850SAkhilesh Sanikop // complete. 36*09537850SAkhilesh Sanikop template <bool has_failure_status> 37*09537850SAkhilesh Sanikop class BlockingCounterImpl { 38*09537850SAkhilesh Sanikop public: BlockingCounterImpl(int initial_count)39*09537850SAkhilesh Sanikop explicit BlockingCounterImpl(int initial_count) 40*09537850SAkhilesh Sanikop : count_(initial_count), job_failed_(false) {} 41*09537850SAkhilesh Sanikop 42*09537850SAkhilesh Sanikop // Increment the counter by |count|. This must be called before Wait() is 43*09537850SAkhilesh Sanikop // called. This must be called from the same thread that will call Wait(). IncrementBy(int count)44*09537850SAkhilesh Sanikop void IncrementBy(int count) { 45*09537850SAkhilesh Sanikop assert(count >= 0); 46*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(mutex_); 47*09537850SAkhilesh Sanikop count_ += count; 48*09537850SAkhilesh Sanikop } 49*09537850SAkhilesh Sanikop 50*09537850SAkhilesh Sanikop // Decrement the counter by 1. This function can be called only when 51*09537850SAkhilesh Sanikop // |has_failure_status| is false (i.e.) when this class is being used with the 52*09537850SAkhilesh Sanikop // |BlockingCounter| alias. Decrement()53*09537850SAkhilesh Sanikop void Decrement() { 54*09537850SAkhilesh Sanikop static_assert(!has_failure_status, ""); 55*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(mutex_); 56*09537850SAkhilesh Sanikop if (--count_ == 0) { 57*09537850SAkhilesh Sanikop condition_.notify_one(); 58*09537850SAkhilesh Sanikop } 59*09537850SAkhilesh Sanikop } 60*09537850SAkhilesh Sanikop 61*09537850SAkhilesh Sanikop // Decrement the counter by 1. This function can be called only when 62*09537850SAkhilesh Sanikop // |has_failure_status| is true (i.e.) when this class is being used with the 63*09537850SAkhilesh Sanikop // |BlockingCounterWithStatus| alias. |job_succeeded| is used to update the 64*09537850SAkhilesh Sanikop // state of |job_failed_|. Decrement(bool job_succeeded)65*09537850SAkhilesh Sanikop void Decrement(bool job_succeeded) { 66*09537850SAkhilesh Sanikop static_assert(has_failure_status, ""); 67*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(mutex_); 68*09537850SAkhilesh Sanikop job_failed_ |= !job_succeeded; 69*09537850SAkhilesh Sanikop if (--count_ == 0) { 70*09537850SAkhilesh Sanikop condition_.notify_one(); 71*09537850SAkhilesh Sanikop } 72*09537850SAkhilesh Sanikop } 73*09537850SAkhilesh Sanikop 74*09537850SAkhilesh Sanikop // Block until the counter becomes 0. This function can be called only once 75*09537850SAkhilesh Sanikop // per object. If |has_failure_status| is true, true is returned if all the 76*09537850SAkhilesh Sanikop // jobs succeeded and false is returned if any of the jobs failed. If 77*09537850SAkhilesh Sanikop // |has_failure_status| is false, this function always returns true. Wait()78*09537850SAkhilesh Sanikop bool Wait() { 79*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(mutex_); 80*09537850SAkhilesh Sanikop condition_.wait(lock, [this]() { return count_ == 0; }); 81*09537850SAkhilesh Sanikop // If |has_failure_status| is false, we simply return true. 82*09537850SAkhilesh Sanikop return has_failure_status ? !job_failed_ : true; 83*09537850SAkhilesh Sanikop } 84*09537850SAkhilesh Sanikop 85*09537850SAkhilesh Sanikop private: 86*09537850SAkhilesh Sanikop std::mutex mutex_; 87*09537850SAkhilesh Sanikop std::condition_variable condition_; 88*09537850SAkhilesh Sanikop int count_ LIBGAV1_GUARDED_BY(mutex_); 89*09537850SAkhilesh Sanikop bool job_failed_ LIBGAV1_GUARDED_BY(mutex_); 90*09537850SAkhilesh Sanikop }; 91*09537850SAkhilesh Sanikop 92*09537850SAkhilesh Sanikop using BlockingCounterWithStatus = BlockingCounterImpl<true>; 93*09537850SAkhilesh Sanikop using BlockingCounter = BlockingCounterImpl<false>; 94*09537850SAkhilesh Sanikop 95*09537850SAkhilesh Sanikop } // namespace libgav1 96*09537850SAkhilesh Sanikop 97*09537850SAkhilesh Sanikop #endif // LIBGAV1_SRC_UTILS_BLOCKING_COUNTER_H_ 98