[PATCH] xfree86: use a thread for the generation of input events

Tiago Vignatti tiago.vignatti at nokia.com
Mon Aug 23 01:17:10 PDT 2010


The current SIGIO signal handler method, used at generation of input events,
has a bunch of oddities. This patch introduces an alternative way using a
thread, which is used to select()s all input device file descriptors.

A mutex was used to control the access of the mi queue by the main and input
threads. Two pipes to emit alert events (such hotplug ones) and guarantee the
proper communication between them was also used.

Co-authored-by: Fernando Carrijo <fcarrijo at freedesktop.org>
Signed-off-by: Tiago Vignatti <tiago.vignatti at nokia.com>
---
 configure.ac                   |    9 +
 dix/main.c                     |   13 ++
 hw/xfree86/common/xf86Events.c |   23 +++
 include/dix-config.h.in        |    3 +
 include/opaque.h               |    4 +
 include/os.h                   |   18 ++
 mi/mieq.c                      |   70 ++++-----
 os/Makefile.am                 |    5 +
 os/WaitFor.c                   |    5 +
 os/connection.c                |    8 +
 os/inputthread.c               |  368 ++++++++++++++++++++++++++++++++++++++++
 11 files changed, 487 insertions(+), 39 deletions(-)
 create mode 100644 os/inputthread.c

diff --git a/configure.ac b/configure.ac
index 9884fa7..bfdf6ac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -477,6 +477,10 @@ AC_ARG_ENABLE(unit-tests,    AS_HELP_STRING([--enable-unit-tests],
 AC_ARG_ENABLE(use-sigio-by-default, AS_HELP_STRING([--enable-use-sigio-by-default]
   [Enable SIGIO input handlers by default (default: $USE_SIGIO_BY_DEFAULT)]),
                                 [USE_SIGIO_BY_DEFAULT=$enableval], [])
+AC_ARG_ENABLE(input-thread,  AS_HELP_STRING([--enable-input-thread],
+  [Use a separate thread for input event generation (default: yes)]),
+                                [INPUT_THREAD=$enableval],
+                                [INPUT_THREAD=yes])
 AC_ARG_WITH(int10,           AS_HELP_STRING([--with-int10=BACKEND], [int10 backend: vm86, x86emu or stub]),
 				[INT10="$withval"],
 				[INT10="$DEFAULT_INT10"])
@@ -1126,6 +1130,11 @@ if test "x$DPMSExtension" = xyes; then
 	AC_DEFINE(DPMSExtension, 1, [Support DPMS extension])
 fi
 
+AM_CONDITIONAL(INPUT_THREAD, [test "x$INPUT_THREAD" = xyes])
+if test "x$INPUT_THREAD" = xyes; then
+       AC_DEFINE(INPUT_THREAD, 1, [Use a separate thread for input event generation])
+fi
+
 if test "x$XCALIBRATE" = xyes && test "$KDRIVE" = yes; then
    AC_DEFINE(XCALIBRATE, 1, [Build XCalibrate extension])
    REQUIRED_MODULES="$REQUIRED_MODULES $XCALIBRATEPROTO"
diff --git a/dix/main.c b/dix/main.c
index 47a932f..f79f16f 100644
--- a/dix/main.c
+++ b/dix/main.c
@@ -111,6 +111,12 @@ Equipment Corporation.
 #include "dispatch.h"		/* InitProcVectors() */
 #endif
 
+#ifndef INPUT_THREAD
+static inline void threaded_input_pre_init(void) {}
+static inline void threaded_input_init(void) {}
+static inline void threaded_input_fini(void) {}
+#endif
+
 #ifdef DPMSExtension
 #include <X11/extensions/dpmsconst.h>
 #include "dpmsproc.h"
@@ -260,6 +266,9 @@ int main(int argc, char *argv[], char *envp[])
 	    InitRootWindow(screenInfo.screens[i]->root);
 
         InitCoreDevices();
+
+	threaded_input_pre_init();
+
 	InitInput(argc, argv);
 	InitAndStartDevices();
 
@@ -288,6 +297,8 @@ int main(int argc, char *argv[], char *envp[])
         
 	NotifyParentProcess();
 
+	threaded_input_init();
+
 	Dispatch();
 
         UndisplayDevices();
@@ -311,6 +322,8 @@ int main(int argc, char *argv[], char *envp[])
 
         CloseInput();
 
+	threaded_input_fini();
+
 	for (i = 0; i < screenInfo.numScreens; i++)
 	    screenInfo.screens[i]->root = NullWindow;
 	CloseDownDevices();
diff --git a/hw/xfree86/common/xf86Events.c b/hw/xfree86/common/xf86Events.c
index 2e82848..9c44f2d 100644
--- a/hw/xfree86/common/xf86Events.c
+++ b/hw/xfree86/common/xf86Events.c
@@ -284,6 +284,19 @@ xf86Wakeup(pointer blockData, int err, pointer pReadmask)
     if (xf86VTSwitchPending()) xf86VTSwitch();
 }
 
+#ifdef INPUT_THREAD
+/*
+ * xf86ThreadReadInput --
+ *    signal handler for the input thread.
+ */
+static void
+xf86ThreadReadInput(void *closure)
+{
+    InputInfoPtr pInfo = (InputInfoPtr) closure;
+
+    pInfo->read_input(pInfo);
+}
+#else
 
 /*
  * xf86SigioReadInput --
@@ -299,6 +312,7 @@ xf86SigioReadInput(int fd, void *closure)
 
     errno = errno_save;
 }
+#endif
 
 /*
  * xf86AddEnabledDevice --
@@ -307,9 +321,14 @@ xf86SigioReadInput(int fd, void *closure)
 void
 xf86AddEnabledDevice(InputInfoPtr pInfo)
 {
+#ifdef INPUT_THREAD
+    threaded_input_register_device(pInfo->fd, xf86ThreadReadInput, pInfo);
+#else
+
     if (!xf86InstallSIGIOHandler (pInfo->fd, xf86SigioReadInput, pInfo)) {
 	AddEnabledDevice(pInfo->fd);
     }
+#endif
 }
 
 /*
@@ -319,9 +338,13 @@ xf86AddEnabledDevice(InputInfoPtr pInfo)
 void
 xf86RemoveEnabledDevice(InputInfoPtr pInfo)
 {
+#ifdef INPUT_THREAD
+    threaded_input_unregister_device(pInfo->fd);
+#else
     if (!xf86RemoveSIGIOHandler (pInfo->fd)) {
 	RemoveEnabledDevice(pInfo->fd);
     }
+#endif
 }
 
 static int *xf86SignalIntercept = NULL;
diff --git a/include/dix-config.h.in b/include/dix-config.h.in
index 6a33264..e25261b 100644
--- a/include/dix-config.h.in
+++ b/include/dix-config.h.in
@@ -435,6 +435,9 @@
 /* Need the strcasestr function. */
 #undef NEED_STRCASESTR
 
+/* Define to 1 to use the input thread */
+#undef INPUT_THREAD
+
 /* Define to 1 if you have the `ffs' function. */
 #undef HAVE_FFS
 
diff --git a/include/opaque.h b/include/opaque.h
index b3c7c70..2d6807f 100644
--- a/include/opaque.h
+++ b/include/opaque.h
@@ -39,6 +39,10 @@ extern _X_EXPORT int MaxClients;
 extern _X_EXPORT volatile char isItTimeToYield;
 extern _X_EXPORT volatile char dispatchException;
 
+#ifdef INPUT_THREAD
+extern int MaxInputDevices;
+#endif
+
 /* bit values for dispatchException */
 #define DE_RESET     1
 #define DE_TERMINATE 2
diff --git a/include/os.h b/include/os.h
index efa202c..0b0113e 100644
--- a/include/os.h
+++ b/include/os.h
@@ -165,6 +165,24 @@ extern _X_EXPORT void MakeClientGrabPervious(ClientPtr /*client*/);
 extern void ListenOnOpenFD(int /* fd */, int /* noxauth */);
 #endif
 
+#ifdef INPUT_THREAD
+typedef void (*read_input)(void*);
+
+extern void threaded_input_pre_init(void);
+
+extern void threaded_input_init(void);
+
+extern void threaded_input_register_device(int /* fd */,
+                                           read_input /* func */,
+                                           void* /* read_input_args */);
+
+extern void threaded_input_unregister_device(int /* fd */);
+
+extern int threaded_input_drain_pipe(void);
+
+extern void threaded_input_fini(void);
+#endif
+
 extern _X_EXPORT CARD32 GetTimeInMillis(void);
 
 extern _X_EXPORT void AdjustWaitForDelay(
diff --git a/mi/mieq.c b/mi/mieq.c
index fa60b40..f68b73b 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -79,10 +79,23 @@ typedef struct _EventQueue {
 
 static EventQueueRec miEventQueue;
 
-#ifdef XQUARTZ
+#if defined XQUARTZ || INPUT_THREAD
 #include  <pthread.h>
 static pthread_mutex_t miEventQueueMutex = PTHREAD_MUTEX_INITIALIZER;
 
+static inline void queue_mutex_lock(void) {
+    pthread_mutex_lock(&miEventQueueMutex);
+}
+
+static inline void queue_mutex_unlock(void) {
+    pthread_mutex_unlock(&miEventQueueMutex);
+}
+#else
+static inline void queue_mutex_lock(void) {}
+static inline void queue_mutex_unlock(void) {}
+#endif
+
+#if defined XQUARTZ
 extern BOOL serverInitComplete;
 extern pthread_mutex_t serverInitCompleteMutex;
 extern pthread_cond_t serverInitCompleteCond;
@@ -96,6 +109,9 @@ static inline void wait_for_server_init(void) {
         pthread_mutex_unlock(&serverInitCompleteMutex);
     }
 }
+
+#else
+static inline void wait_for_server_init(void) {}
 #endif
 
 Bool
@@ -151,10 +167,8 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
     int                    evlen;
     Time                   time;
 
-#ifdef XQUARTZ
     wait_for_server_init();
-    pthread_mutex_lock(&miEventQueueMutex);
-#endif
+    queue_mutex_lock();
 
     CHECKEVENT(e);
 
@@ -178,9 +192,7 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
                 xorg_backtrace();
                 stuck = 1;
             }
-#ifdef XQUARTZ
-            pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+            queue_mutex_unlock();
 	        return;
         }
         stuck = 0;
@@ -195,9 +207,7 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
         if (!evt->event)
         {
             ErrorF("[mi] Running out of memory. Tossing event.\n");
-#ifdef XQUARTZ
-            pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+            queue_mutex_unlock();
             return;
         }
     }
@@ -217,39 +227,30 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
 
     miEventQueue.lastMotion = isMotion;
     miEventQueue.tail = (oldtail + 1) % QUEUE_SIZE;
-#ifdef XQUARTZ
-    pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+
+    queue_mutex_unlock();
 }
 
 void
 mieqSwitchScreen(DeviceIntPtr pDev, ScreenPtr pScreen, Bool fromDIX)
 {
-#ifdef XQUARTZ
-    pthread_mutex_lock(&miEventQueueMutex);
-#endif
+    queue_mutex_lock();
     EnqueueScreen(pDev) = pScreen;
     if (fromDIX)
         DequeueScreen(pDev) = pScreen;
-#ifdef XQUARTZ
-    pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+    queue_mutex_unlock();
 }
 
 void
 mieqSetHandler(int event, mieqHandler handler)
 {
-#ifdef XQUARTZ
-    pthread_mutex_lock(&miEventQueueMutex);
-#endif
+    queue_mutex_lock();
     if (handler && miEventQueue.handlers[event])
         ErrorF("[mi] mieq: warning: overriding existing handler %p with %p for "
                "event %d\n", miEventQueue.handlers[event], handler, event);
 
     miEventQueue.handlers[event] = handler;
-#ifdef XQUARTZ
-    pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+    queue_mutex_unlock();
 }
 
 /**
@@ -430,10 +431,8 @@ mieqProcessInputEvents(void)
     DeviceIntPtr dev = NULL,
                  master = NULL;
 
-#ifdef XQUARTZ
-    pthread_mutex_lock(&miEventQueueMutex);
-#endif
-    
+    queue_mutex_lock();
+
     while (miEventQueue.head != miEventQueue.tail) {
         e = &miEventQueue.events[miEventQueue.head];
 
@@ -452,9 +451,7 @@ mieqProcessInputEvents(void)
 
         miEventQueue.head = (miEventQueue.head + 1) % QUEUE_SIZE;
 
-#ifdef XQUARTZ
-        pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+        queue_mutex_unlock();
 
         master  = (dev && !IsMaster(dev) && dev->u.master) ? dev->u.master : NULL;
 
@@ -474,12 +471,7 @@ mieqProcessInputEvents(void)
         if (event->any.type == ET_Motion && master)
             miPointerUpdateSprite(dev);
 
-#ifdef XQUARTZ
-        pthread_mutex_lock(&miEventQueueMutex);
-#endif
+        queue_mutex_lock();
     }
-#ifdef XQUARTZ
-    pthread_mutex_unlock(&miEventQueueMutex);
-#endif
+    queue_mutex_unlock();
 }
-
diff --git a/os/Makefile.am b/os/Makefile.am
index 3e4f2c5..52a4494 100644
--- a/os/Makefile.am
+++ b/os/Makefile.am
@@ -5,6 +5,7 @@ AM_CFLAGS = $(DIX_CFLAGS) $(SHA1_CFLAGS)
 SECURERPC_SRCS = rpcauth.c
 XDMCP_SRCS = xdmcp.c
 STRLCAT_SRCS = strlcat.c strlcpy.c
+INPUT_THREAD_SRCS = inputthread.c
 
 # Build a convenience library liblog.la that will be added into
 # libos.la. The split is done so that log.c can be built with
@@ -47,6 +48,10 @@ if NEED_STRLCAT
 libos_la_SOURCES += $(STRLCAT_SRCS)
 endif
 
+if INPUT_THREAD
+libos_la_SOURCES += $(INPUT_THREAD_SRCS)
+endif
+
 EXTRA_DIST = $(SECURERPC_SRCS) $(INTERNALMALLOC_SRCS) \
      $(XDMCP_SRCS) $(STRLCAT_SRCS)
 
diff --git a/os/WaitFor.c b/os/WaitFor.c
index e663004..9c64482 100644
--- a/os/WaitFor.c
+++ b/os/WaitFor.c
@@ -75,6 +75,10 @@ SOFTWARE.
 #include "dpmsproc.h"
 #endif
 
+#ifndef INPUT_THREAD
+static inline void threaded_input_drain_pipe(void) {}
+#endif
+
 #ifdef WIN32
 /* Error codes from windows sockets differ from fileio error codes  */
 #undef EINTR
@@ -226,6 +230,7 @@ WaitForSomething(int *pClientsReady)
 	}
 	else 
 	{
+	    threaded_input_drain_pipe();
 	    i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
 	}
 	selecterr = GetErrno();
diff --git a/os/connection.c b/os/connection.c
index 77910be..81340b6 100644
--- a/os/connection.c
+++ b/os/connection.c
@@ -145,6 +145,10 @@ int MaxClients = 0;
 Bool NewOutputPending;		/* not yet attempted to write some new output */
 Bool AnyClientsWriteBlocked;	/* true if some client blocked on write */
 
+#ifdef INPUT_THREAD
+int MaxInputDevices = 0;
+#endif
+
 static Bool RunFromSmartParent;	/* send SIGUSR1 to parent process */
 Bool PartialNetwork;	/* continue even if unable to bind all addrs */
 static Pid_t ParentProcess;
@@ -302,6 +306,10 @@ InitConnectionLimits(void)
     if (lastfdesc > MAXSELECT)
 	lastfdesc = MAXSELECT;
 
+#ifdef INPUT_THREAD
+    MaxInputDevices = lastfdesc;
+#endif
+
     if (lastfdesc > MAXCLIENTS)
     {
 	lastfdesc = MAXCLIENTS;
diff --git a/os/inputthread.c b/os/inputthread.c
new file mode 100644
index 0000000..3ed78b5
--- /dev/null
+++ b/os/inputthread.c
@@ -0,0 +1,368 @@
+/* inputthread.c -- Threaded generation of input events.
+ *
+ * Copyright 2010 Fernando Carrijo <fcarrijo at freedesktop org>
+ * Copyright 2007-2010 Tiago Vignatti <vignatti at freedesktop org>
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Except as contained in this notice, the name of the copyright holder(s)
+ * and author(s) shall not be used in advertising or otherwise to promote
+ * the sale, use or other dealings in this Software without prior written
+ * authorization from the copyright holder(s) and author(s).
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <X11/Xpoll.h>
+#include "inputstr.h"
+#include "opaque.h"
+
+/**
+ * An input device as seen by the threaded input facility
+ */
+typedef struct _threaded_input_device {
+    struct _threaded_input_device *next;
+    void (*read_input_proc)(void*);
+    void *read_input_args;
+    int fd;
+} threaded_input_device;
+
+/**
+ * The threaded input facility.
+ *
+ * For now, we have one instance for all input devices.
+ */
+typedef struct {
+    pthread_t thread;
+    threaded_input_device *devs;
+    fd_set fds;
+    int read_pipe;
+    int write_pipe;
+} threaded_input_info;
+
+static threaded_input_info *threaded_input;
+
+static int hotplugPipeRead = -1;
+static int hotplugPipeWrite = -1;
+
+/**
+ * Notify a thread about the availability of new asynchronously enqueued input
+ * events.
+ *
+ * @see WaitForSomething()
+ * @see threaded_input_drain_pipe()
+ */
+static void
+threaded_input_fill_pipe(int write_head)
+{
+    int ret;
+    char byte = 0;
+    fd_set write_pipe;
+
+    FD_ZERO(&write_pipe);
+
+    while (1) {
+        ret = write(write_head, &byte, 1);
+        if (!ret)
+            FatalError("threaded-input: write() returned 0");
+        if (ret > 0) {
+            break;
+        }
+        if (errno != EAGAIN)
+            FatalError("threaded-input: filling pipe");
+
+        DebugF("threaded-input: pipe full\n");
+        FD_SET(write_head, &write_pipe);
+        Select(write_head + 1, NULL, &write_pipe, NULL, NULL);
+    }
+}
+
+/**
+ * Consume eventual notifications left by a thread.
+ *
+ * @see WaitForSomething()
+ * @see thraded_input_fill_pipe()
+ */
+static int
+threaded_input_read_pipe(int read_head)
+{
+    int ret, array[10];
+
+    ret = read(read_head, &array, sizeof(array));
+    if (ret >= 0)
+        return ret;
+
+    if (errno != EAGAIN)
+        FatalError("threaded-input: draining pipe (%d)", errno);
+
+    return 1;
+}
+
+int
+threaded_input_drain_pipe(void)
+{
+    return threaded_input_read_pipe(threaded_input->read_pipe);
+}
+
+/**
+ * Register an input device in the threaded input facility
+ *
+ * @param fd File descriptor which identifies the input device
+ * @param read_input_proc Procedure used to read input from the device
+ * @param read_input_args Arguments to be consumed by the above procedure
+ */
+void
+threaded_input_register_device(int fd,
+                               void (*read_input_proc) (void*),
+                               void *read_input_args)
+{
+    threaded_input_device *new;
+
+    new = malloc(sizeof(threaded_input_device));
+    if (new == NULL)
+        FatalError("threaded-input: could not register device");
+
+    new->fd = fd;
+    new->read_input_proc = read_input_proc;
+    new->read_input_args = read_input_args;
+    new->next = threaded_input->devs;
+
+    threaded_input->devs = new;
+    FD_SET(fd, &threaded_input->fds);
+
+    threaded_input_fill_pipe(hotplugPipeWrite);
+    DebugF("threaded-input: registered device %d\n", fd);
+}
+
+/**
+ * Unregister a device in the threaded input facility, returning silently if
+ * it was not previously registered.
+ *
+ * @param fd File descriptor which identifies the input device
+ */
+void
+threaded_input_unregister_device(int fd)
+{
+    threaded_input_device *prev, *dev;
+
+
+    /* return silently if input thread is already finished (e.g., at
+     * DisableDevice time, evdev tries to call this function again through
+     * xf86RemoveEnabledDevice */
+    if (!threaded_input)
+        return;
+
+    prev = NULL;
+    dev = threaded_input->devs;
+    while (dev != NULL) {
+        if (dev->fd == fd)
+            break;
+        prev = dev;
+        dev = dev->next;
+    }
+
+    /* fd doesn't match with registered devices. Return silently */
+    if (dev == NULL)
+        return;
+
+    if (prev == NULL)
+        threaded_input->devs = dev->next;
+    else
+        prev->next = dev->next;
+
+    FD_CLR(fd, &threaded_input->fds);
+    dev->read_input_proc = NULL;
+    dev->read_input_args = NULL;
+    dev->fd = 0;
+    dev = 0;
+    free(dev);
+
+    threaded_input_fill_pipe(hotplugPipeWrite);
+    DebugF("threaded-input: unregistered device: %d\n", fd);
+}
+
+/**
+ * The workhorse of threaded input event generation.
+ *
+ * Or if you prefer: The WaitForSomething for input devices. :)
+ *
+ * Runs in parallel with the server main thread, listening to input devices in
+ * an endless loop. Whenever new input data is made available, calls the
+ * proper device driver's routines which are ultimately responsible for the
+ * generation of input events.
+ *
+ * @see threaded_input_pre_init()
+ * @see threaded_input_init()
+ */
+
+static void*
+threaded_input_do_work(void *arg)
+{
+    fd_set ready_fds;
+    threaded_input_device *dev;
+
+    FD_ZERO(&ready_fds);
+
+    while (1)
+    {
+        XFD_COPYSET(&threaded_input->fds, &ready_fds);
+        FD_SET(hotplugPipeRead, &ready_fds);
+
+        DebugF("threaded-input: do_work waiting for devices\n");
+
+        if (Select(MaxInputDevices, &ready_fds, NULL, NULL, NULL) < 0)
+        {
+            if (errno == EINVAL)
+            {
+                FatalError("threaded-input: do_work (%s)", strerror(errno));
+            }
+            else if (errno != EINTR)
+            {
+                ErrorF("threaded-input: do_work (%s)\n", strerror(errno));
+            }
+        }
+
+        DebugF("threaded-input: do_work generating events\n");
+        /* Call the device drivers to generate input events for us */
+        for (dev = threaded_input->devs; dev != NULL; dev = dev->next)
+            if (FD_ISSET(dev->fd, &ready_fds) && dev->read_input_proc)
+                dev->read_input_proc(dev->read_input_args);
+
+        /* Kick main thread to process the generated input events and drain
+         * events from hotplug pipe */
+        threaded_input_fill_pipe(threaded_input->write_pipe);
+        threaded_input_read_pipe(hotplugPipeRead);
+    }
+}
+
+/**
+ * Pre-initialize the facility used for threaded generation of input events
+ *
+ */
+void
+threaded_input_pre_init(void)
+{
+    int fds[2], hotplugPipe[2];
+
+    if (pipe(fds) < 0)
+        FatalError("threaded-input: could not create pipe");
+
+     if (pipe(hotplugPipe) < 0)
+        FatalError("threaded-input: could not create pipe");
+
+    threaded_input = malloc(sizeof(threaded_input_info));
+    if (!threaded_input)
+        FatalError("threaded-input: could not allocate memory");
+
+    threaded_input->thread = 0;
+    threaded_input->devs = NULL;
+    FD_ZERO(&threaded_input->fds);
+
+    /* By making read head non-blocking, we ensure that while the main thread
+     * is busy servicing client requests, the dedicated input thread can work
+     * in parallel.
+     */
+    threaded_input->read_pipe = fds[0];
+    fcntl(threaded_input->read_pipe, F_SETFL, O_NONBLOCK);
+    AddGeneralSocket(threaded_input->read_pipe);
+    threaded_input->write_pipe = fds[1];
+
+    hotplugPipeRead = hotplugPipe[0];
+    fcntl(hotplugPipeRead, F_SETFL, O_NONBLOCK);
+    hotplugPipeWrite = hotplugPipe[1];
+}
+
+/**
+ * Start the threaded generation of input events. This routine complements what
+ * was previously done by threaded_input_pre_init(), being only responsible for
+ * creating the dedicated input thread.
+ *
+ */
+void
+threaded_input_init(void)
+{
+    pthread_attr_t attr;
+
+    pthread_attr_init(&attr);
+
+    /* For OSes that differentiate between processes and threads, the following
+     * lines have sense. Linux uses the 1:1 thread model. The scheduler handles
+     * every thread as a normal process. Therefore this probably has no meaning
+     * if we are under Linux.
+     */
+    if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0)
+        ErrorF("threaded-input: error setting thread scope\n");
+
+    if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
+        ErrorF("threaded-input: error setting thread stack size\n");
+
+    DebugF("threaded-input: creating thread\n");
+    pthread_create(&threaded_input->thread, &attr,
+                   &threaded_input_do_work, NULL);
+
+    pthread_attr_destroy (&attr);
+}
+
+/**
+ * Stop the threaded generation of input events
+ *
+ * This function is supposed to be called at server shutdown time only.
+ */
+void
+threaded_input_fini(void)
+{
+    threaded_input_device *dev, *tmp;
+
+    pthread_cancel(threaded_input->thread);
+    pthread_join(threaded_input->thread, NULL);
+
+    for (dev = threaded_input->devs; dev != NULL; dev = tmp) {
+        tmp = dev->next;
+        dev->next = NULL;
+        dev->read_input_proc = NULL;
+        dev->read_input_args = NULL;
+        FD_CLR(dev->fd, &threaded_input->fds);
+        dev->fd = -1;
+        free(dev);
+    }
+    threaded_input->devs = NULL;
+    FD_ZERO(&threaded_input->fds);
+
+    RemoveGeneralSocket(threaded_input->read_pipe);
+    close(threaded_input->read_pipe);
+    close(threaded_input->write_pipe);
+    threaded_input->read_pipe = -1;
+    threaded_input->write_pipe = -1;
+
+    close(hotplugPipeRead);
+    close(hotplugPipeWrite);
+    hotplugPipeRead = -1;
+    hotplugPipeWrite = -1;
+
+    free(threaded_input);
+    threaded_input = NULL;
+}
-- 
1.7.1.226.g770c5



More information about the xorg-devel mailing list