LogCabin
Core/ConditionVariable.cc
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 <cstring>
00018 
00019 #include "Core/ConditionVariable.h"
00020 #include "Core/Debug.h"
00021 
00022 namespace LogCabin {
00023 namespace Core {
00024 
00025 ConditionVariable::ConditionVariable()
00026     : cv()
00027     , callback()
00028     , notificationCount(0)
00029     , lastWaitUntil()
00030 {
00031     // Note that all these pthread_cond* functions return errors in the return
00032     // code, NOT using errno.
00033     pthread_condattr_t cvattr;
00034     int r = pthread_condattr_init(&cvattr);
00035     if (r != 0)
00036         PANIC("pthread_condattr_init failed: %s", strerror(r));
00037     // glibc as of 2014 only supports CLOCK_MONOTONIC or CLOCK_REALTIME here,
00038     // others return EINVAL. See glibc/nptl/pthread_condattr_setclock.c.
00039     r = pthread_condattr_setclock(&cvattr, Core::Time::STEADY_CLOCK_ID);
00040     if (r != 0)
00041         PANIC("pthread_condattr_setclock failed: %s", strerror(r));
00042     r = pthread_cond_init(&cv, &cvattr);
00043     if (r != 0)
00044         PANIC("pthread_cond_init failed: %s", strerror(r));
00045     r = pthread_condattr_destroy(&cvattr);
00046     if (r != 0)
00047         PANIC("pthread_condattr_destroy failed: %s", strerror(r));
00048 }
00049 
00050 ConditionVariable::~ConditionVariable()
00051 {
00052     int r = pthread_cond_destroy(&cv);
00053     if (r != 0)
00054         PANIC("pthread_cond_destroy failed: %s", strerror(r));
00055 }
00056 
00057 void
00058 ConditionVariable::notify_one()
00059 {
00060     ++notificationCount;
00061     int r = pthread_cond_signal(&cv);
00062     if (r != 0)
00063         PANIC("pthread_cond_signal failed: %s", strerror(r));
00064 }
00065 
00066 void
00067 ConditionVariable::notify_all()
00068 {
00069     ++notificationCount;
00070     int r = pthread_cond_broadcast(&cv);
00071     if (r != 0)
00072         PANIC("pthread_cond_broadcast failed: %s", strerror(r));
00073 }
00074 
00075 void
00076 ConditionVariable::wait(std::unique_lock<std::mutex>& lockGuard)
00077 {
00078     if (callback) {
00079         lockGuard.unlock();
00080         callback();
00081         lockGuard.lock();
00082     } else {
00083         pthread_mutex_t* mutex = lockGuard.mutex()->native_handle();
00084         int r = pthread_cond_wait(&cv, mutex);
00085         if (r != 0)
00086             PANIC("pthread_cond_wait failed: %s", strerror(r));
00087     }
00088 }
00089 
00090 void
00091 ConditionVariable::wait(std::unique_lock<Core::Mutex>& lockGuard)
00092 {
00093     Core::Mutex& mutex(*lockGuard.mutex());
00094     if (mutex.callback)
00095         mutex.callback();
00096     assert(lockGuard);
00097     std::unique_lock<std::mutex> stdLockGuard(mutex.m,
00098                                               std::adopt_lock_t());
00099     lockGuard.release();
00100     wait(stdLockGuard);
00101     assert(stdLockGuard);
00102     lockGuard = std::unique_lock<Core::Mutex>(mutex, std::adopt_lock_t());
00103     stdLockGuard.release();
00104     if (mutex.callback)
00105         mutex.callback();
00106 }
00107 
00108 void
00109 ConditionVariable::wait_until(
00110             std::unique_lock<std::mutex>& lockGuard,
00111             const Core::Time::SteadyClock::time_point& abs_time)
00112 {
00113     lastWaitUntil = abs_time;
00114     if (callback) {
00115         lockGuard.unlock();
00116         callback();
00117         lockGuard.lock();
00118     } else {
00119         Core::Time::SteadyClock::time_point now =
00120             Core::Time::SteadyClock::now();
00121         Core::Time::SteadyClock::time_point wake =
00122             std::min(abs_time, now + std::chrono::hours(1));
00123         if (wake < now)
00124             return;
00125         struct timespec wakespec = Core::Time::makeTimeSpec(wake);
00126         pthread_mutex_t* mutex = lockGuard.mutex()->native_handle();
00127         int r = pthread_cond_timedwait(&cv, mutex, &wakespec);
00128         switch (r) {
00129             case 0:
00130                 break;
00131             case ETIMEDOUT:
00132                 break;
00133             default:
00134                 PANIC("pthread_cond_timedwait failed: %s", strerror(r));
00135         }
00136     }
00137 }
00138 
00139 } // namespace LogCabin::Core
00140 } // namespace LogCabin
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines