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
35 void
libinput_timer_init(struct libinput_timer * timer,struct libinput * libinput,const char * timer_name,void (* timer_func)(uint64_t now,void * timer_func_data),void * timer_func_data)36 libinput_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 }
47
48 void
libinput_timer_destroy(struct libinput_timer * timer)49 libinput_timer_destroy(struct libinput_timer *timer)
50 {
51 if (timer->link.prev != NULL && timer->link.prev != NULL &&
52 !list_empty(&timer->link)) {
53 log_bug_libinput(timer->libinput,
54 "timer: %s has not been cancelled\n",
55 timer->timer_name);
56 assert(!"timer not cancelled");
57 }
58 free(timer->timer_name);
59 }
60
61 static void
libinput_timer_arm_timer_fd(struct libinput * libinput)62 libinput_timer_arm_timer_fd(struct libinput *libinput)
63 {
64 int r;
65 struct libinput_timer *timer;
66 struct itimerspec its = { { 0, 0 }, { 0, 0 } };
67 uint64_t earliest_expire = UINT64_MAX;
68
69 list_for_each(timer, &libinput->timer.list, link) {
70 if (timer->expire < earliest_expire)
71 earliest_expire = timer->expire;
72 }
73
74 if (earliest_expire != UINT64_MAX) {
75 its.it_value.tv_sec = earliest_expire / ms2us(1000);
76 its.it_value.tv_nsec = (earliest_expire % ms2us(1000)) * 1000;
77 }
78
79 r = timerfd_settime(libinput->timer.fd, TFD_TIMER_ABSTIME, &its, NULL);
80 if (r)
81 log_error(libinput, "timer: timerfd_settime error: %s\n", strerror(errno));
82
83 libinput->timer.next_expiry = earliest_expire;
84 }
85
86 void
libinput_timer_set_flags(struct libinput_timer * timer,uint64_t expire,uint32_t flags)87 libinput_timer_set_flags(struct libinput_timer *timer,
88 uint64_t expire,
89 uint32_t flags)
90 {
91 #ifndef NDEBUG
92 uint64_t now = libinput_now(timer->libinput);
93 if (expire < now) {
94 if ((flags & TIMER_FLAG_ALLOW_NEGATIVE) == 0)
95 log_bug_client(timer->libinput,
96 "timer %s: scheduled expiry is in the past (-%dms), your system is too slow\n",
97 timer->timer_name,
98 us2ms(now - expire));
99 } else if ((expire - now) > ms2us(5000)) {
100 log_bug_libinput(timer->libinput,
101 "timer %s: offset more than 5s, now %d expire %d\n",
102 timer->timer_name,
103 us2ms(now), us2ms(expire));
104 }
105 #endif
106
107 assert(expire);
108
109 if (!timer->expire)
110 list_insert(&timer->libinput->timer.list, &timer->link);
111
112 timer->expire = expire;
113 libinput_timer_arm_timer_fd(timer->libinput);
114 }
115
116 void
libinput_timer_set(struct libinput_timer * timer,uint64_t expire)117 libinput_timer_set(struct libinput_timer *timer, uint64_t expire)
118 {
119 libinput_timer_set_flags(timer, expire, TIMER_FLAG_NONE);
120 }
121
122 void
libinput_timer_cancel(struct libinput_timer * timer)123 libinput_timer_cancel(struct libinput_timer *timer)
124 {
125 if (!timer->expire)
126 return;
127
128 timer->expire = 0;
129 list_remove(&timer->link);
130 libinput_timer_arm_timer_fd(timer->libinput);
131 }
132
133 static void
libinput_timer_handler(struct libinput * libinput,uint64_t now)134 libinput_timer_handler(struct libinput *libinput , uint64_t now)
135 {
136 struct libinput_timer *timer;
137
138 restart:
139 list_for_each(timer, &libinput->timer.list, link) {
140 if (timer->expire == 0)
141 continue;
142
143 if (timer->expire <= now) {
144 /* Clear the timer before calling timer_func,
145 as timer_func may re-arm it */
146 libinput_timer_cancel(timer);
147 timer->timer_func(now, timer->timer_func_data);
148
149 /*
150 * Restart the loop. We can't use
151 * list_for_each_safe() here because that only
152 * allows removing one (our) timer per timer_func.
153 * But the timer func may trigger another unrelated
154 * timer to be cancelled and removed, causing a
155 * segfault.
156 */
157 goto restart;
158 }
159 }
160 }
161
162 static void
libinput_timer_dispatch(void * data)163 libinput_timer_dispatch(void *data)
164 {
165 struct libinput *libinput = data;
166 uint64_t now;
167 uint64_t discard;
168 int r;
169
170 r = read(libinput->timer.fd, &discard, sizeof(discard));
171 if (r == -1 && errno != EAGAIN)
172 log_bug_libinput(libinput,
173 "timer: error %d reading from timerfd (%s)",
174 errno,
175 strerror(errno));
176
177 now = libinput_now(libinput);
178 if (now == 0)
179 return;
180
181 libinput_timer_handler(libinput, now);
182 }
183
184 int
libinput_timer_subsys_init(struct libinput * libinput)185 libinput_timer_subsys_init(struct libinput *libinput)
186 {
187 libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC,
188 TFD_CLOEXEC | TFD_NONBLOCK);
189 if (libinput->timer.fd < 0)
190 return -1;
191
192 list_init(&libinput->timer.list);
193
194 libinput->timer.source = libinput_add_fd(libinput,
195 libinput->timer.fd,
196 libinput_timer_dispatch,
197 libinput);
198 if (!libinput->timer.source) {
199 close(libinput->timer.fd);
200 return -1;
201 }
202
203 return 0;
204 }
205
206 void
libinput_timer_subsys_destroy(struct libinput * libinput)207 libinput_timer_subsys_destroy(struct libinput *libinput)
208 {
209 #ifndef NDEBUG
210 if (!list_empty(&libinput->timer.list)) {
211 struct libinput_timer *t;
212
213 list_for_each(t, &libinput->timer.list, link) {
214 log_bug_libinput(libinput,
215 "timer: %s still present on shutdown\n",
216 t->timer_name);
217 }
218 }
219 #endif
220
221 /* All timer users should have destroyed their timers now */
222 assert(list_empty(&libinput->timer.list));
223
224 libinput_remove_source(libinput, libinput->timer.source);
225 close(libinput->timer.fd);
226 }
227
228 /**
229 * For a caller calling libinput_dispatch() only infrequently, we may have a
230 * timer expiry *and* a later input event waiting in the pipe. We cannot
231 * guarantee that we read the timer expiry first, so this hook exists to
232 * flush any timers.
233 *
234 * Assume 'now' is the current time check if there is a current timer expiry
235 * before this time. If so, trigger the timer func.
236 */
237 void
libinput_timer_flush(struct libinput * libinput,uint64_t now)238 libinput_timer_flush(struct libinput *libinput, uint64_t now)
239 {
240 if (libinput->timer.next_expiry == 0 ||
241 libinput->timer.next_expiry > now)
242 return;
243
244 libinput_timer_handler(libinput, now);
245 }
246