From 342a43e7b2ceec1dd200bffa24b052fec33f5bdc Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Fri, 30 Jul 2010 15:29:38 +0200 Subject: Introduce threading In order to enable deferred events from grail, but the event processing in a separate thread, and have the calling thread return as quickly as possible. Signed-off-by: Henrik Rydberg --- src/Makefile.am | 2 +- src/grail-api.c | 75 ++++++++++++++++++++++++++++++++++-- src/mtdev-plumbing.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/touch-dev.c | 36 +++++++++++++++++- src/touch-engine.c | 7 ++-- 5 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 src/mtdev-plumbing.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 67f930a..5835897 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,7 @@ libgrail_la_SOURCES = \ grail-recognizer.c \ grail-api.c -AM_CFLAGS = $(CWARNFLAGS) -std=c99 +AM_CFLAGS = $(CWARNFLAGS) -pthread INCLUDES = -I$(top_srcdir)/include/ diff --git a/src/grail-api.c b/src/grail-api.c index 19b66aa..e3b8125 100644 --- a/src/grail-api.c +++ b/src/grail-api.c @@ -1,6 +1,8 @@ #include "grail-inserter.h" #include "grail-recognizer.h" #include +#include +#include #include #include #include @@ -9,12 +11,48 @@ #include #include "evbuf.h" +static const pthread_mutex_t c_mutex = PTHREAD_MUTEX_INITIALIZER; +static const pthread_cond_t c_wait = PTHREAD_COND_INITIALIZER; + struct grail_impl { struct touch_dev dev; struct touch_engine engine; struct evbuf evbuf; + int fd; + int pull, finished; + pthread_mutex_t mutex; + pthread_cond_t wait; + pthread_t thread; }; +/* + * While not finished, lock, wait for condition, extract the running + * state, unlock, then process the event queue. + * + * The processing may in principle take seconds to complete without + * locking up the writer side. + */ +static void *grail_thread(void *priv) +{ + struct grail_impl *x = priv; + int pull, finished = 0; + sigset_t block; + sigemptyset(&block); + sigaddset(&block, SIGIO); + pthread_sigmask(SIG_BLOCK, &block, NULL); + while (!finished) { + pthread_mutex_lock(&x->mutex); + pthread_cond_wait(&x->wait, &x->mutex); + pull = x->pull; + finished = x->finished; + x->pull = 0; + pthread_mutex_unlock(&x->mutex); + if (!finished && pull) + touch_dev_process(&x->dev); + } + pthread_exit(NULL); +} + static void tp_event(struct touch_engine *engine, const struct input_event *ev) { @@ -66,10 +104,18 @@ int grail_open(struct grail *ge, int fd) ret = touch_dev_open(&x->dev, fd); if (ret) goto freegru; + x->fd = fd; + x->mutex = c_mutex; + x->wait = c_wait; + ret = pthread_create(&x->thread, NULL, grail_thread, x); + if (ret) + goto freetouch; + ge->impl = x; touch_engine_init(&x->engine, tp_event, tp_sync, ge); touch_engine_attach(&x->engine, &x->dev); - ge->impl = x; return 0; + freetouch: + touch_dev_close(&x->dev, fd); freegru: gru_destroy(ge); freedev: @@ -82,6 +128,11 @@ int grail_open(struct grail *ge, int fd) void grail_close(struct grail *ge, int fd) { struct grail_impl *x = ge->impl; + void *status; + pthread_mutex_lock(&x->mutex); + x->finished = 1; + pthread_cond_signal (&x->wait); + pthread_mutex_unlock(&x->mutex); touch_engine_detach(&x->engine, &x->dev); touch_dev_close(&x->dev, fd); gru_destroy(ge); @@ -93,12 +144,28 @@ void grail_close(struct grail *ge, int fd) int grail_idle(struct grail *ge, int fd, int ms) { struct grail_impl *x = ge->impl; - return touch_dev_idle(&x->dev, fd, ms); + int ret; + pthread_mutex_lock(&x->mutex); + ret = touch_dev_idle(&x->dev, fd, ms); + pthread_mutex_unlock(&x->mutex); + return ret; } +/* + * First extract all input events to a lockless circular buffer, then, + * under lock, signal the worker thread. + * + * This function only blocks temporarily while signalling the worker + * thread. + */ int grail_pull(struct grail *ge, int fd) { struct grail_impl *x = ge->impl; - return touch_dev_pull(&x->dev, fd); + int finished = touch_dev_fetch(&x->dev, fd) < 0; + pthread_mutex_lock(&x->mutex); + x->pull = 1; + x->finished = finished; + pthread_cond_signal(&x->wait); + pthread_mutex_unlock(&x->mutex); + return 1; } - diff --git a/src/mtdev-plumbing.h b/src/mtdev-plumbing.h new file mode 100644 index 0000000..c1d141e --- /dev/null +++ b/src/mtdev-plumbing.h @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * mtdev - Multitouch Protocol Translation Library (MIT license) + * + * Copyright (C) 2010 Henrik Rydberg + * Copyright (C) 2010 Canonical Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************/ + +#ifndef _MTDEV_PLUMBING_H +#define _MTDEV_PLUMBING_H + +#include + +/** + * mtdev_init - initialize mtdev converter + * @dev: the mtdev to initialize + * + * Sets up the internal data structures. + * + * Returns zero on success, negative error number otherwise. + */ +int mtdev_init(struct mtdev *dev); + +/** + * mtdev_configure - configure the mtdev converter + * @dev: the mtdev to configure + * @fd: file descriptor of the kernel device + * + * Reads the device properties to set up the protocol capabilities. + * If preferred, this can be done by hand, omitting this call. + * + * Returns zero on success, negative error number otherwise. + */ +int mtdev_configure(struct mtdev *dev, int fd); + +/** + * mtdev_fetch_event - fetch an event from the kernel device + * @dev: the mtdev in use + * @fd: file descriptor of the kernel device + * @ev: the kernel input event to fill + * + * Fetch a kernel event from the kernel device. The read operation + * behaves as dictated by the file descriptor; if O_NONBLOCK is not + * set, the read will block until an event is available. + * + * On success, returns the number of events read (0 or 1). Otherwise, + * a standard negative error number is returned. + */ +int mtdev_fetch_event(struct mtdev *dev, int fd, struct input_event *ev); + +/** + * mtdev_put_event - put an event into the converter + * @dev: the mtdev in use + * @ev: the kernel input event to put + * + * Put a kernel event into the mtdev converter. The event should + * come straight from the device. + * + * This call does not block; if the buffer becomes full, older events + * are dropped. The buffer is guaranteed to handle several complete MT + * packets. + */ +void mtdev_put_event(struct mtdev *dev, const struct input_event *ev); + +/** + * mtdev_empty - check if there are events to get + * @dev: the mtdev in use + * + * Returns true if the processed event queue is empty, false otherwise. + */ +int mtdev_empty(struct mtdev *dev); + +/** + * mtdev_get_event - get processed events from mtdev + * @dev: the mtdev in use + * @ev: the input event to fill + * + * Get a processed event from mtdev. The events appear as if they came + * from a type B device emitting MT slot events. + * + * The queue must be non-empty before calling this function. + */ +void mtdev_get_event(struct mtdev *dev, struct input_event* ev); + +#endif diff --git a/src/touch-dev.c b/src/touch-dev.c index dc23baa..b7efb35 100644 --- a/src/touch-dev.c +++ b/src/touch-dev.c @@ -26,6 +26,8 @@ #include #include #include +#include "evbuf.h" +#include "mtdev-plumbing.h" #define DIM_TOUCH 32 @@ -35,9 +37,27 @@ struct touch_dev_impl { int status; touch_prop_mask_t mask[1]; touch_prop_t prop[DIM_TOUCH_PROP]; + struct evbuf evbuf; struct mtdev mtdev; }; +static int touch_get_fetched(struct touch_dev_impl *x, + struct input_event* ev, int ev_max) +{ + struct input_event kev; + int count = 0; + while (count < ev_max) { + while (mtdev_empty(&x->mtdev)) { + if (evbuf_empty(&x->evbuf)) + return count; + evbuf_get(&x->evbuf, &kev); + mtdev_put_event(&x->mtdev, &kev); + } + mtdev_get_event(&x->mtdev, &ev[count++]); + } + return count; +} + static inline int has_any_prop(const touch_prop_mask_t *mask) { return mask[0] != 0; @@ -149,12 +169,24 @@ int touch_dev_idle(struct touch_dev *dev, int fd, int ms) return mtdev_idle(&dev->impl->mtdev, fd, ms); } -int touch_dev_pull(struct touch_dev *dev, int fd) +int touch_dev_fetch(struct touch_dev *dev, int fd) +{ + struct touch_dev_impl *x = dev->impl; + struct input_event ev; + int ret, count = 0; + while ((ret = mtdev_fetch_event(&x->mtdev, fd, &ev)) > 0) { + evbuf_put(&x->evbuf, &ev); + count++; + } + return count > 0 ? count : ret; +} + +int touch_dev_process(struct touch_dev *dev) { struct touch_dev_impl *x = dev->impl; struct input_event ev; int ret, count = 0, consumed; - while ((ret = mtdev_get(&x->mtdev, fd, &ev, 1)) > 0) { + while ((ret = touch_get_fetched(x, &ev, 1)) > 0) { consumed = 0; if (ev.type == EV_SYN) { if (ev.code == SYN_REPORT) diff --git a/src/touch-engine.c b/src/touch-engine.c index 8e43faf..28b68da 100644 --- a/src/touch-engine.c +++ b/src/touch-engine.c @@ -40,7 +40,8 @@ static void update_frame(struct touch_frame *frame, struct touch *touch = &frame->touch[slot]; touch->active = active; if (mask && prop) { - for (int i = 0; i < DIM_TOUCH_PROP; i++) + int i; + for (i = 0; i < DIM_TOUCH_PROP; i++) if (has_prop(mask, i)) touch->prop[i] = prop[i]; } @@ -48,10 +49,10 @@ static void update_frame(struct touch_frame *frame, static void finalize_frame(struct touch_frame *frame, touch_time_t time) { - int last = -1; + int i, last = -1; frame->ntouch = 0; frame->slot = -1; - for (int i = 0; i < DIM_TOUCH_PROP; i++) { + for (i = 0; i < DIM_TOUCH_PROP; i++) { frame->touch[i].next = -1; if (frame->touch[i].active) { if (last < 0) -- cgit v1.2.3