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 <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