1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef threading_ConditionVariable_h 8 #define threading_ConditionVariable_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/PlatformConditionVariable.h" 12 #include "mozilla/TimeStamp.h" 13 14 #include <stdint.h> 15 #include <utility> 16 #ifndef XP_WIN 17 # include <pthread.h> 18 #endif 19 20 #include "threading/LockGuard.h" 21 #include "threading/Mutex.h" 22 23 namespace js { 24 25 template <class T> 26 class ExclusiveData; 27 28 enum class CVStatus { NoTimeout, Timeout }; 29 30 template <typename T> 31 using UniqueLock = LockGuard<T>; 32 33 // A poly-fill for std::condition_variable. 34 class ConditionVariable { 35 public: 36 struct PlatformData; 37 38 ConditionVariable() = default; 39 ~ConditionVariable() = default; 40 41 // Wake one thread that is waiting on this condition. notify_one()42 void notify_one() { impl_.notify_one(); } 43 44 // Wake all threads that are waiting on this condition. notify_all()45 void notify_all() { impl_.notify_all(); } 46 47 // Block the current thread of execution until this condition variable is 48 // woken from another thread via notify_one or notify_all. wait(Mutex & lock)49 void wait(Mutex& lock) { 50 #ifdef DEBUG 51 lock.preUnlockChecks(); 52 #endif 53 impl_.wait(lock.impl_); 54 #ifdef DEBUG 55 lock.preLockChecks(); 56 lock.postLockChecks(); 57 #endif 58 } wait(UniqueLock<Mutex> & lock)59 void wait(UniqueLock<Mutex>& lock) { wait(lock.lock); } 60 61 // As with |wait|, block the current thread of execution until woken from 62 // another thread. This method will resume waiting once woken until the given 63 // Predicate |pred| evaluates to true. 64 template <typename Predicate> wait(UniqueLock<Mutex> & lock,Predicate pred)65 void wait(UniqueLock<Mutex>& lock, Predicate pred) { 66 while (!pred()) { 67 wait(lock); 68 } 69 } 70 71 // Block the current thread of execution until woken from another thread, or 72 // the given absolute time is reached. The given absolute time is evaluated 73 // when this method is called, so will wake up after (abs_time - now), 74 // independent of system clock changes. While insulated from clock changes, 75 // this API is succeptible to the issues discussed above wait_for. wait_until(UniqueLock<Mutex> & lock,const mozilla::TimeStamp & abs_time)76 CVStatus wait_until(UniqueLock<Mutex>& lock, 77 const mozilla::TimeStamp& abs_time) { 78 return wait_for(lock, abs_time - mozilla::TimeStamp::Now()); 79 } 80 81 // As with |wait_until|, block the current thread of execution until woken 82 // from another thread, or the given absolute time is reached. This method 83 // will resume waiting once woken until the given Predicate |pred| evaluates 84 // to true. 85 template <typename Predicate> wait_until(UniqueLock<Mutex> & lock,const mozilla::TimeStamp & abs_time,Predicate pred)86 bool wait_until(UniqueLock<Mutex>& lock, const mozilla::TimeStamp& abs_time, 87 Predicate pred) { 88 while (!pred()) { 89 if (wait_until(lock, abs_time) == CVStatus::Timeout) { 90 return pred(); 91 } 92 } 93 return true; 94 } 95 96 // Block the current thread of execution until woken from another thread, or 97 // the given time duration has elapsed. Given that the system may be 98 // interrupted between the callee and the actual wait beginning, this call 99 // has a minimum granularity of the system's scheduling interval, and may 100 // encounter substantially longer delays, depending on system load. wait_for(UniqueLock<Mutex> & lock,const mozilla::TimeDuration & rel_time)101 CVStatus wait_for(UniqueLock<Mutex>& lock, 102 const mozilla::TimeDuration& rel_time) { 103 #ifdef DEBUG 104 lock.lock.preUnlockChecks(); 105 #endif 106 CVStatus res = 107 impl_.wait_for(lock.lock.impl_, rel_time) == mozilla::CVStatus::Timeout 108 ? CVStatus::Timeout 109 : CVStatus::NoTimeout; 110 #ifdef DEBUG 111 lock.lock.preLockChecks(); 112 lock.lock.postLockChecks(); 113 #endif 114 return res; 115 } 116 117 // As with |wait_for|, block the current thread of execution until woken from 118 // another thread or the given time duration has elapsed. This method will 119 // resume waiting once woken until the given Predicate |pred| evaluates to 120 // true. 121 template <typename Predicate> wait_for(UniqueLock<Mutex> & lock,const mozilla::TimeDuration & rel_time,Predicate pred)122 bool wait_for(UniqueLock<Mutex>& lock, const mozilla::TimeDuration& rel_time, 123 Predicate pred) { 124 return wait_until(lock, mozilla::TimeStamp::Now() + rel_time, 125 std::move(pred)); 126 } 127 128 private: 129 ConditionVariable(const ConditionVariable&) = delete; 130 ConditionVariable& operator=(const ConditionVariable&) = delete; 131 template <class T> 132 friend class ExclusiveWaitableData; 133 134 mozilla::detail::ConditionVariableImpl impl_; 135 }; 136 137 } // namespace js 138 139 #endif // threading_ConditionVariable_h 140