1/* 2 * Copyright © 2014-2015 Red Hat, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24#include "config.h" 25 26#include <assert.h> 27#include <errno.h> 28#include <string.h> 29#include <sys/timerfd.h> 30#include <unistd.h> 31 32#include "libinput-private.h" 33#include "timer.h" 34 35void 36libinput_timer_init(struct libinput_timer *timer, 37 struct libinput *libinput, 38 const char *timer_name, 39 void (*timer_func)(uint64_t now, void *timer_func_data), 40 void *timer_func_data) 41{ 42 timer->libinput = libinput; 43 timer->timer_name = safe_strdup(timer_name); 44 timer->timer_func = timer_func; 45 timer->timer_func_data = timer_func_data; 46 /* at most 5 "expiry in the past" log messages per hour */ 47 ratelimit_init(&libinput->timer.expiry_in_past_limit, 48 s2us(60 * 60), 5); 49} 50 51void 52libinput_timer_destroy(struct libinput_timer *timer) 53{ 54 if (timer->link.prev != NULL && timer->link.next != NULL && 55 !list_empty(&timer->link)) { 56 log_bug_libinput(timer->libinput, 57 "timer: %s has not been cancelled\n", 58 timer->timer_name); 59 assert(!"timer not cancelled"); 60 } 61 free(timer->timer_name); 62} 63 64static void 65libinput_timer_arm_timer_fd(struct libinput *libinput) 66{ 67 int r; 68 struct libinput_timer *timer; 69 struct itimerspec its = { { 0, 0 }, { 0, 0 } }; 70 uint64_t earliest_expire = UINT64_MAX; 71 72 list_for_each(timer, &libinput->timer.list, link) { 73 if (timer->expire < earliest_expire) 74 earliest_expire = timer->expire; 75 } 76 77 if (earliest_expire != UINT64_MAX) { 78 its.it_value.tv_sec = earliest_expire / ms2us(1000); 79 its.it_value.tv_nsec = (earliest_expire % ms2us(1000)) * 1000; 80 } 81 82 r = timerfd_settime(libinput->timer.fd, TFD_TIMER_ABSTIME, &its, NULL); 83 if (r) 84 log_error(libinput, "timer: timerfd_settime error: %s\n", strerror(errno)); 85 86 libinput->timer.next_expiry = earliest_expire; 87} 88 89void 90libinput_timer_set_flags(struct libinput_timer *timer, 91 uint64_t expire, 92 uint32_t flags) 93{ 94#ifndef NDEBUG 95 /* We only warn if we're more than 20ms behind */ 96 const uint64_t timer_warning_limit = ms2us(20); 97 uint64_t now = libinput_now(timer->libinput); 98 if (expire < now) { 99 if ((flags & TIMER_FLAG_ALLOW_NEGATIVE) == 0 && 100 now - expire > timer_warning_limit) 101 log_bug_client_ratelimit(timer->libinput, 102 &timer->libinput->timer.expiry_in_past_limit, 103 "timer %s: scheduled expiry is in the past (-%dms), your system is too slow\n", 104 timer->timer_name, 105 us2ms(now - expire)); 106 } else if ((expire - now) > ms2us(5000)) { 107 log_bug_libinput(timer->libinput, 108 "timer %s: offset more than 5s, now %d expire %d\n", 109 timer->timer_name, 110 us2ms(now), us2ms(expire)); 111 } 112#endif 113 114 assert(expire); 115 116 if (!timer->expire) 117 list_insert(&timer->libinput->timer.list, &timer->link); 118 119 timer->expire = expire; 120 libinput_timer_arm_timer_fd(timer->libinput); 121} 122 123void 124libinput_timer_set(struct libinput_timer *timer, uint64_t expire) 125{ 126 libinput_timer_set_flags(timer, expire, TIMER_FLAG_NONE); 127} 128 129void 130libinput_timer_cancel(struct libinput_timer *timer) 131{ 132 if (!timer->expire) 133 return; 134 135 timer->expire = 0; 136 list_remove(&timer->link); 137 libinput_timer_arm_timer_fd(timer->libinput); 138} 139 140static void 141libinput_timer_handler(struct libinput *libinput , uint64_t now) 142{ 143 struct libinput_timer *timer; 144 145restart: 146 list_for_each_safe(timer, &libinput->timer.list, link) { 147 if (timer->expire == 0) 148 continue; 149 150 if (timer->expire <= now) { 151 /* Clear the timer before calling timer_func, 152 as timer_func may re-arm it */ 153 libinput_timer_cancel(timer); 154 timer->timer_func(now, timer->timer_func_data); 155 156 /* 157 * Restart the loop. We can't use 158 * list_for_each_safe() here because that only 159 * allows removing one (our) timer per timer_func. 160 * But the timer func may trigger another unrelated 161 * timer to be cancelled and removed, causing a 162 * segfault. 163 */ 164 goto restart; 165 } 166 } 167} 168 169static void 170libinput_timer_dispatch(void *data) 171{ 172 struct libinput *libinput = data; 173 uint64_t now; 174 uint64_t discard; 175 int r; 176 177 r = read(libinput->timer.fd, &discard, sizeof(discard)); 178 if (r == -1 && errno != EAGAIN) 179 log_bug_libinput(libinput, 180 "timer: error %d reading from timerfd (%s)", 181 errno, 182 strerror(errno)); 183 184 now = libinput_now(libinput); 185 if (now == 0) 186 return; 187 188 libinput_timer_handler(libinput, now); 189} 190 191int 192libinput_timer_subsys_init(struct libinput *libinput) 193{ 194 libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, 195 TFD_CLOEXEC | TFD_NONBLOCK); 196 if (libinput->timer.fd < 0) 197 return -1; 198 199 list_init(&libinput->timer.list); 200 201 libinput->timer.source = libinput_add_fd(libinput, 202 libinput->timer.fd, 203 libinput_timer_dispatch, 204 libinput); 205 if (!libinput->timer.source) { 206 close(libinput->timer.fd); 207 return -1; 208 } 209 210 return 0; 211} 212 213void 214libinput_timer_subsys_destroy(struct libinput *libinput) 215{ 216#ifndef NDEBUG 217 if (!list_empty(&libinput->timer.list)) { 218 struct libinput_timer *t; 219 220 list_for_each(t, &libinput->timer.list, link) { 221 log_bug_libinput(libinput, 222 "timer: %s still present on shutdown\n", 223 t->timer_name); 224 } 225 } 226#endif 227 228 /* All timer users should have destroyed their timers now */ 229 assert(list_empty(&libinput->timer.list)); 230 231 libinput_remove_source(libinput, libinput->timer.source); 232 close(libinput->timer.fd); 233} 234 235/** 236 * For a caller calling libinput_dispatch() only infrequently, we may have a 237 * timer expiry *and* a later input event waiting in the pipe. We cannot 238 * guarantee that we read the timer expiry first, so this hook exists to 239 * flush any timers. 240 * 241 * Assume 'now' is the current time check if there is a current timer expiry 242 * before this time. If so, trigger the timer func. 243 */ 244void 245libinput_timer_flush(struct libinput *libinput, uint64_t now) 246{ 247 if (libinput->timer.next_expiry == 0 || 248 libinput->timer.next_expiry > now) 249 return; 250 251 libinput_timer_handler(libinput, now); 252} 253