LogCabin
|
00001 /* Copyright (c) 2011-2014 Stanford University 00002 * Copyright (c) 2015 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 #include <sys/epoll.h> 00019 #include <sys/timerfd.h> 00020 #include <unistd.h> 00021 00022 #include "Core/Debug.h" 00023 #include "Event/Loop.h" 00024 #include "Event/Timer.h" 00025 00026 namespace LogCabin { 00027 namespace Event { 00028 00029 namespace { 00030 00031 /// Helper for constructor. 00032 int 00033 createTimerFd() 00034 { 00035 int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); 00036 if (fd < 0) { 00037 PANIC("Could not create timerfd: %s", strerror(errno)); 00038 } 00039 return fd; 00040 } 00041 00042 } // anonymous namespace 00043 00044 //// class Timer::Monitor //// 00045 00046 Timer::Monitor::Monitor(Event::Loop& eventLoop, Timer& timer) 00047 : File::Monitor(eventLoop, timer, EPOLLIN|EPOLLET) 00048 { 00049 } 00050 00051 Timer::Monitor::~Monitor() 00052 { 00053 } 00054 00055 //// class Timer //// 00056 00057 Timer::Timer() 00058 : Event::File(createTimerFd()) 00059 { 00060 } 00061 00062 Timer::~Timer() 00063 { 00064 } 00065 00066 void 00067 Timer::schedule(uint64_t nanoseconds) 00068 { 00069 // avoid accidental de-schedules: epoll's semantics are that a timer for 0 00070 // seconds and 0 nanoseconds will never fire. 00071 if (nanoseconds == 0) 00072 nanoseconds = 1; 00073 00074 const uint64_t nanosPerSecond = 1000 * 1000 * 1000; 00075 struct itimerspec newValue; 00076 memset(&newValue, 0, sizeof(newValue)); 00077 newValue.it_value.tv_sec = nanoseconds / nanosPerSecond; 00078 newValue.it_value.tv_nsec = nanoseconds % nanosPerSecond; 00079 int r = timerfd_settime(fd, 0, &newValue, NULL); 00080 if (r != 0) { 00081 PANIC("Could not set timer to +%luns: %s", 00082 nanoseconds, 00083 strerror(errno)); 00084 } 00085 } 00086 00087 void 00088 Timer::scheduleAbsolute(Core::Time::SteadyClock::time_point timeout) 00089 { 00090 static_assert(Core::Time::STEADY_CLOCK_ID == CLOCK_MONOTONIC, 00091 "scheduleAbsolute assumes SteadyClock uses CLOCK_MONOTONIC"); 00092 struct itimerspec newValue; 00093 memset(&newValue, 0, sizeof(newValue)); 00094 newValue.it_value = Core::Time::makeTimeSpec(timeout); 00095 // Will get EINVAL on negative times, might as well use 0.000000001. 00096 if (newValue.it_value.tv_sec < 0) { 00097 newValue.it_value.tv_sec = 0; 00098 newValue.it_value.tv_nsec = 1; 00099 } 00100 // avoid accidental de-schedules: epoll's semantics are that a timer for 0 00101 // seconds and 0 nanoseconds will never fire. 00102 if (newValue.it_value.tv_sec == 0 && newValue.it_value.tv_nsec == 0) 00103 newValue.it_value.tv_nsec = 1; 00104 00105 int r = timerfd_settime(fd, TFD_TIMER_ABSTIME, &newValue, NULL); 00106 if (r != 0) { 00107 PANIC("Could not set timer to %ld.%09ld: %s", 00108 newValue.it_value.tv_sec, 00109 newValue.it_value.tv_nsec, 00110 strerror(errno)); 00111 } 00112 } 00113 00114 void 00115 Timer::deschedule() 00116 { 00117 struct itimerspec newValue; 00118 memset(&newValue, 0, sizeof(newValue)); 00119 int r = timerfd_settime(fd, 0, &newValue, NULL); 00120 if (r != 0) 00121 PANIC("Could not deschedule timer: %s", strerror(errno)); 00122 } 00123 00124 bool 00125 Timer::isScheduled() const 00126 { 00127 // Unfortunately, timerfd_gettime seems to return 0 when an absolute time 00128 // has already elapsed. See note on isScheduled() in header file. 00129 struct itimerspec currentValue; 00130 int r = timerfd_gettime(fd, ¤tValue); 00131 if (r != 0) 00132 PANIC("Could not get timer: %s", strerror(errno)); 00133 return (currentValue.it_value.tv_sec != 0 || 00134 currentValue.it_value.tv_nsec != 0); 00135 } 00136 00137 void 00138 Timer::handleFileEvent(uint32_t events) 00139 { 00140 handleTimerEvent(); 00141 } 00142 00143 } // namespace LogCabin::Event 00144 } // namespace LogCabin