LogCabin
|
00001 /* Copyright (c) 2012-2014 Stanford University 00002 * Copyright (c) 2014 Diego Ongaro 00003 * 00004 * Permission to use, copy, modify, and distribute this software for any 00005 * purpose with or without fee is hereby granted, provided that the above 00006 * copyright notice and this permission notice appear in all copies. 00007 * 00008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00015 */ 00016 00017 #include <functional> 00018 #include <pthread.h> 00019 00020 #include "Core/CompatAtomic.h" 00021 #include "Core/Mutex.h" 00022 #include "Core/Time.h" 00023 00024 #ifndef LOGCABIN_CORE_CONDITIONVARIABLE_H 00025 #define LOGCABIN_CORE_CONDITIONVARIABLE_H 00026 00027 namespace LogCabin { 00028 namespace Core { 00029 00030 /** 00031 * Similar to std::condition_variable but with improvements for testing, 00032 * support for monotonic clocks, and less buggy. 00033 * 00034 * For testing, you can set a callback to be called when the condition variable 00035 * is waited on; instead of waiting, this callback will be called. This 00036 * callback can, for example, change some shared state so that the calling 00037 * thread's condition is satisfied. It also counts how many times the condition 00038 * variable has been notified. 00039 * 00040 * The interface to this class is a subset of std::condition_variable: 00041 * - wait_for isn't exposed since it doesn't make much sense to me in light 00042 * of spurious interrupts. 00043 * - wait_until returns void instead of cv_status since it's almost always 00044 * clearer to check whether timeout has elapsed explicitly. Also, gcc 4.4 00045 * doesn't have cv_status and uses bool instead. 00046 * 00047 * This class also goes through some trouble so that it can be used with 00048 * std::unique_lock<Core::Mutex>, since normal condition variables may only be 00049 * used with std::unique_lock<std::mutex>. 00050 * 00051 * All waiting on this class is done using a monotonic clock internally, so it 00052 * will not be affected by time jumps from, e.g., NTP. This implies that, if 00053 * you're actually waiting for a specific system time to come around, you might 00054 * end up with surprising behavior. If you're wondering why, Linux/POSIX 00055 * condition variables use a single clock for all waiters, whereas C++11 uses 00056 * an impossible-to-implement interface where different waiters can use 00057 * different clocks. 00058 */ 00059 class ConditionVariable { 00060 public: 00061 ConditionVariable(); 00062 ~ConditionVariable(); 00063 void notify_one(); 00064 void notify_all(); 00065 void wait(std::unique_lock<std::mutex>& lockGuard); 00066 void wait(std::unique_lock<Core::Mutex>& lockGuard); 00067 00068 // std::mutex and SteadyClock 00069 void 00070 wait_until(std::unique_lock<std::mutex>& lockGuard, 00071 const Core::Time::SteadyClock::time_point& abs_time); 00072 00073 // std::mutex and any clock: calls std::mutex and SteadyClock variant 00074 template<typename Clock, typename Duration> 00075 void 00076 wait_until(std::unique_lock<std::mutex>& lockGuard, 00077 const std::chrono::time_point<Clock, Duration>& abs_time) { 00078 std::chrono::time_point<Clock, Duration> now = Clock::now(); 00079 std::chrono::time_point<Clock, Duration> wake = abs_time; 00080 // Clamp to wake to [now - hour, now + hour] to avoid overflow. 00081 // See related http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58931 00082 if (abs_time < now) 00083 wake = now - std::chrono::hours(1); 00084 else if (abs_time > now + std::chrono::hours(1)) 00085 wake = now + std::chrono::hours(1); 00086 Core::Time::SteadyClock::time_point steadyNow = 00087 Core::Time::SteadyClock::now(); 00088 Core::Time::SteadyClock::time_point steadyWake = 00089 steadyNow + (wake - now); 00090 wait_until(lockGuard, steadyWake); 00091 } 00092 00093 // Core::Mutex and any clock: calls std::mutex and any clock variant 00094 template<typename Clock, typename Duration> 00095 void 00096 wait_until(std::unique_lock<Core::Mutex>& lockGuard, 00097 const std::chrono::time_point<Clock, Duration>& abs_time) { 00098 Core::Mutex& mutex(*lockGuard.mutex()); 00099 if (mutex.callback) 00100 mutex.callback(); 00101 assert(lockGuard); 00102 std::unique_lock<std::mutex> stdLockGuard(mutex.m, 00103 std::adopt_lock_t()); 00104 lockGuard.release(); 00105 wait_until(stdLockGuard, abs_time); 00106 assert(stdLockGuard); 00107 lockGuard = std::unique_lock<Core::Mutex>(mutex, std::adopt_lock_t()); 00108 stdLockGuard.release(); 00109 if (mutex.callback) 00110 mutex.callback(); 00111 } 00112 00113 private: 00114 /// Underlying condition variable. 00115 pthread_cond_t cv; 00116 /** 00117 * This function will be called with the lock released during every 00118 * invocation of wait/wait_until. No wait will actually occur; this is only 00119 * used for unit testing. 00120 */ 00121 std::function<void()> callback; 00122 public: 00123 /** 00124 * The number of times this condition variable has been notified. 00125 */ 00126 std::atomic<uint64_t> notificationCount; 00127 /** 00128 * In the last call to wait_until, the timeout that the caller provided (in 00129 * terms of SteadyClock). 00130 * This is used in some unit tests to check that timeouts are set 00131 * correctly. 00132 */ 00133 Core::Time::SteadyClock::time_point lastWaitUntil; 00134 }; 00135 00136 } // namespace LogCabin::Core 00137 } // namespace LogCabin 00138 00139 #endif // LOGCABIN_CORE_CONDITIONVARIABLE_H