• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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