xref: /third_party/libinput/src/timer.c (revision a46c0ec8)
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