LogCabin
Core/ConditionVariable.h
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines