LogCabin
Event/Timer.cc
Go to the documentation of this file.
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, &currentValue);
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines