[PATCH libinput 1/3] Add a timer subsystem

Jonas Ådahl jadahl at gmail.com
Wed Jun 4 15:18:59 PDT 2014


On Wed, Jun 04, 2014 at 05:26:34PM +0200, Hans de Goede wrote:
> Currently we are using DIY timers in the touchpad softbutton and tap handling
> code, and at least the softbutton code gets its wrong. It uses one timer-fd
> per touchpad to set a timeout per touch, which means that if a timeout is
> set for 100ms from now for touch 1, and then a timeout gets set 50 ms later
> for 200 ms from now, then the timeout for touch 1 will come 150 ms too late.
> 
> This commits adds a proper timer subsystem so that we've one place to deal
> with timer handling, and so that we can only get it wrong (well hopefully
> we get it right) in one place.
> 
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
>  src/Makefile.am        |   2 +
>  src/libinput-private.h |   9 ++-
>  src/libinput.c         |  11 +++-
>  src/timer.c            | 149 +++++++++++++++++++++++++++++++++++++++++++++++++
>  src/timer.h            |  56 +++++++++++++++++++
>  5 files changed, 224 insertions(+), 3 deletions(-)
>  create mode 100644 src/timer.c
>  create mode 100644 src/timer.h
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index efb089a..8b56348 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -22,6 +22,8 @@ libinput_la_SOURCES =			\
>  	path.c				\
>  	udev-seat.c			\
>  	udev-seat.h			\
> +	timer.c				\
> +	timer.h				\
>  	../include/linux/input.h
>  
>  libinput_la_LIBADD = $(MTDEV_LIBS) \
> diff --git a/src/libinput-private.h b/src/libinput-private.h
> index 0e92e68..ed8190d 100644
> --- a/src/libinput-private.h
> +++ b/src/libinput-private.h
> @@ -28,6 +28,8 @@
>  #include "libinput.h"
>  #include "libinput-util.h"
>  
> +struct libinput_source;
> +
>  struct libinput_interface_backend {
>  	int (*resume)(struct libinput *libinput);
>  	void (*suspend)(struct libinput *libinput);
> @@ -40,6 +42,12 @@ struct libinput {
>  
>  	struct list seat_list;
>  
> +	struct {
> +		struct list list;
> +		struct libinput_source *source;
> +		int fd;
> +	} timer;
> +
>  	struct libinput_event **events;
>  	size_t events_count;
>  	size_t events_len;
> @@ -79,7 +87,6 @@ struct libinput_device {
>  
>  typedef void (*libinput_source_dispatch_t)(void *data);
>  
> -struct libinput_source;
>  
>  #define log_debug(...) log_msg(LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
>  #define log_info(...) log_msg(LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
> diff --git a/src/libinput.c b/src/libinput.c
> index 6b7e8b8..e74d29f 100644
> --- a/src/libinput.c
> +++ b/src/libinput.c
> @@ -33,6 +33,7 @@
>  #include "libinput.h"
>  #include "libinput-private.h"
>  #include "evdev.h"
> +#include "timer.h"
>  
>  struct libinput_source {
>  	libinput_source_dispatch_t dispatch;
> @@ -485,6 +486,12 @@ libinput_init(struct libinput *libinput,
>  	list_init(&libinput->source_destroy_list);
>  	list_init(&libinput->seat_list);
>  
> +	if (libinput_timer_subsys_init(libinput) != 0) {
> +		free(libinput->events);
> +		close(libinput->epoll_fd);
> +		return -1;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -521,8 +528,6 @@ libinput_destroy(struct libinput *libinput)
>  	while ((event = libinput_get_event(libinput)))
>  	       libinput_event_destroy(event);
>  
> -	libinput_drop_destroyed_sources(libinput);
> -
>  	free(libinput->events);
>  
>  	list_for_each_safe(seat, next_seat, &libinput->seat_list, link) {
> @@ -534,6 +539,8 @@ libinput_destroy(struct libinput *libinput)
>  		libinput_seat_destroy(seat);
>  	}
>  
> +	libinput_timer_subsys_exit(libinput);
> +	libinput_drop_destroyed_sources(libinput);
>  	close(libinput->epoll_fd);
>  	free(libinput);
>  }
> diff --git a/src/timer.c b/src/timer.c
> new file mode 100644
> index 0000000..349a6c9
> --- /dev/null
> +++ b/src/timer.c
> @@ -0,0 +1,149 @@
> +/*
> + * Copyright © 2014 Red Hat, Inc.
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission.  The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose.  It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <sys/timerfd.h>
> +#include <unistd.h>
> +
> +#include "libinput-private.h"
> +#include "timer.h"
> +
> +void
> +libinput_timer_init(struct libinput_timer *timer, struct libinput *libinput,
> +		    void (*timer_func)(uint64_t now, void *timer_func_data),
> +		    void *timer_func_data)
> +{
> +	timer->libinput = libinput;
> +	timer->timer_func = timer_func;
> +	timer->timer_func_data = timer_func_data;
> +}
> +
> +static void
> +libinput_timer_arm_timer_fd(struct libinput *libinput)
> +{
> +	int r;
> +	struct libinput_timer *timer;
> +	struct itimerspec its = {
> +		.it_interval.tv_sec = 0,
> +		.it_interval.tv_nsec = 0,
> +		.it_value.tv_sec = 0,
> +		.it_value.tv_nsec = 0,
> +	};

This could be shortened to: struct itimerspec its = { 0 };

> +	uint64_t earliest_expire = UINT64_MAX;
> +
> +	list_for_each(timer, &libinput->timer.list, list) {
> +		if (timer->expire < earliest_expire)
> +			earliest_expire = timer->expire;
> +	}
> +
> +	if (earliest_expire != UINT64_MAX) {
> +		its.it_value.tv_sec = earliest_expire / 1000;
> +		its.it_value.tv_nsec = (earliest_expire % 1000) * 1000 * 1000;
> +	}
> +
> +	r = timerfd_settime(libinput->timer.fd, TFD_TIMER_ABSTIME, &its, NULL);
> +	if (r)
> +		log_error("timerfd_settime error: %s\n", strerror(errno));
> +}
> +
> +void
> +libinput_timer_set(struct libinput_timer *timer, uint64_t expire)
> +{
> +	assert(expire);
> +
> +	if (!timer->expire)
> +		list_insert(&timer->libinput->timer.list, &timer->list);
> +
> +	timer->expire = expire;
> +	libinput_timer_arm_timer_fd(timer->libinput);
> +}
> +
> +void
> +libinput_timer_cancel(struct libinput_timer *timer)
> +{
> +	if (!timer->expire)
> +		return;
> +
> +	timer->expire = 0;
> +	list_remove(&timer->list);
> +	libinput_timer_arm_timer_fd(timer->libinput);
> +}
> +
> +static void
> +libinput_timer_handler(void *data)
> +{
> +	struct libinput *libinput = data;
> +	struct libinput_timer *timer, *tmp;
> +	struct timespec ts;
> +	uint64_t now;
> +	int r;
> +
> +	r = clock_gettime(CLOCK_MONOTONIC, &ts);
> +	if (r) {
> +		log_error("clock_gettime error: %s\n", strerror(errno));
> +		return;
> +	}
> +
> +	now = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000;
> +
> +	list_for_each_safe(timer, tmp, &libinput->timer.list, list) {
> +		if (timer->expire <= now) {
> +			/* Clear the timer before calling timer_func,
> +			   as timer_func may re-arm it */
> +			libinput_timer_cancel(timer);
> +			timer->timer_func(now, timer->timer_func_data);
> +		}
> +	}
> +}
> +
> +int
> +libinput_timer_subsys_init(struct libinput *libinput)
> +{
> +	libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
> +	if (libinput->timer.fd < 0)
> +		return -1;
> +
> +	list_init(&libinput->timer.list);
> +
> +	libinput->timer.source = libinput_add_fd(libinput,
> +						 libinput->timer.fd,
> +						 libinput_timer_handler,
> +						 libinput);
> +	if (!libinput->timer.source) {
> +		close(libinput->timer.fd);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +void
> +libinput_timer_subsys_exit(struct libinput *libinput)

For consistency, this should be _destroy() instead of _exit().

> +{
> +	/* All timer uses should have destroyed their timers now */
> +	assert(list_empty(&libinput->timer.list));
> +
> +	libinput_remove_source(libinput, libinput->timer.source);
> +	close(libinput->timer.fd);
> +}
> diff --git a/src/timer.h b/src/timer.h
> new file mode 100644
> index 0000000..fb2e9b2
> --- /dev/null
> +++ b/src/timer.h
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright © 2014 Red Hat, Inc.
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission.  The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose.  It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#ifndef TIMER_H
> +#define TIMER_H
> +
> +#include <stdint.h>
> +
> +struct libinput;
> +
> +struct libinput_timer {
> +	struct libinput *libinput;
> +	struct list list;

This should probably be called "link", as its not a list, but a link in
a linked list.

> +	uint64_t expire;
> +	void (*timer_func)(uint64_t now, void *timer_func_data);
> +	void *timer_func_data;
> +};
> +
> +void
> +libinput_timer_init(struct libinput_timer *timer, struct libinput *libinput,
> +		    void (*timer_func)(uint64_t now, void *timer_func_data),
> +		    void *timer_func_data);
> +
> +/* Set timer expire time, in ms CLOCK_MONOTONIC */
> +void
> +libinput_timer_set(struct libinput_timer *timer, uint64_t expire);
> +
> +void
> +libinput_timer_cancel(struct libinput_timer *timer);
> +
> +int
> +libinput_timer_subsys_init(struct libinput *libinput);
> +
> +void
> +libinput_timer_subsys_exit(struct libinput *libinput);
> +
> +#endif
> -- 
> 2.0.0
> 

Except for those comments above, this looks good to me.


Jonas

> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel



More information about the wayland-devel mailing list