diff options
author | Tiago Vignatti <tiago.vignatti@nokia.com> | 2010-07-09 21:04:20 +0300 |
---|---|---|
committer | Tiago Vignatti <tiago.vignatti@nokia.com> | 2010-07-11 23:58:06 +0300 |
commit | 2fb21af30c35c4118bf1d6fe42ff49ff4e4a9b07 (patch) | |
tree | 066c4c3232f74442e501b6e478e5e6b9ce8f43d8 | |
parent | 2307ab5bc9365ebbe04568edb7c7620a23689b70 (diff) |
os: inputthread: the input thread implementation
Signed-off-by: Tiago Vignatti <tiago.vignatti@nokia.com>
-rw-r--r-- | os/inputthread.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/os/inputthread.c b/os/inputthread.c new file mode 100644 index 000000000..470370603 --- /dev/null +++ b/os/inputthread.c @@ -0,0 +1,223 @@ +/* inputthread.c -- The input thread implementation. + * + * Copyright 2007-2008 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 <pthread.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <errno.h> +#include <X11/Xpoll.h> + +#include "inputstr.h" +#include "opaque.h" + + +typedef struct _ThreadDeviceFunc { + void (*f) (void *); + int fd; + void *closure; +} ThreadDeviceFunc; + +ThreadDeviceFunc ThreadDevFunc[MAXDEVICES]; + +extern fd_set InputThreadFd; +extern int NumDevicesThreaded; +extern int InputThreadReadPipe; +extern int InputThreadWritePipe; + +static pid_t tid_generation; + +int InputReadPipe = -1; +int InputWritePipe = -1; + +/* + * Create connections in the input thread. Must be called by the + * DDX implementation. + */ +_X_EXPORT void +AddEnabledDeviceThreaded(int fd, void (*f)(void *), void *closure) +{ + FD_SET(fd, &InputThreadFd); + + ThreadDevFunc[NumDevicesThreaded].fd = fd; + ThreadDevFunc[NumDevicesThreaded].f = f; + ThreadDevFunc[NumDevicesThreaded].closure = closure; + + NumDevicesThreaded++; + + DebugF("Input thread: Using PTHREAD for input device %d\n", fd); +} + +/* + * Input events routed through another thread/process can have bad effects on + * latency because we can't guarantee that it will get scheduled at the right + * moment. Although this is hard to see happening with the current + * implementation, we must design something to avoid it. One way to improve + * the responsiveness is to give a high priority to the input thread and also + * adjust the CPU scheduling. + * A detailed discussion can be found here: + * http://vignatti.wordpress.com/2008/08/07/priorities-and-scheduling-hints-for-x-server-threads/ + * + * TODO: incorporate this with cgroup, a feature which allows to limit the + * amount of CPU time real-time processes and threads may consume. + */ +#if 0 +static void +AdjustThreadScheduling(int tid) +{ + struct sched_param param; + int policy = SCHED_FIFO; + + + pthread_t p_tid = pthread_self(); + + param.sched_priority = sched_get_priority_max(policy); + + if (pthread_setschedparam(p_tid, policy, ¶m) != 0) + perror("error setting thread scheduling"); + +#ifdef THREAD_DEBUG + sched_getparam(tid, ¶m); + ErrorF("thread %d: policy: %d, priority: %d\n", tid, + sched_getscheduler(tid_generation), param.sched_priority); +#endif +} +#endif + +/* + * @return exit code for the child process which currently is none. The child + * process exits only when the main thread (parent) send a signal or a + * fatal error occurs. + */ +static int +WaitForInput(void* argument) +{ + int i; + fd_set InputDevices; + int i_pipe[2]; + + + tid_generation = syscall(__NR_gettid); + + /* AdjustThreadScheduling(tid_generation); */ + + FD_ZERO(&InputDevices); + + /* The "communication channel" between the input and main thread */ + if (pipe(i_pipe) < 0) { + perror("pipe"); + exit(-1); + } + + /* Make pipes nonblocking */ + fcntl(i_pipe[0], F_SETFL, O_NONBLOCK); + fcntl(i_pipe[1], F_SETFL, O_NONBLOCK); + + InputReadPipe = i_pipe[0]; + InputWritePipe = i_pipe[1]; + + while (1) + { + XFD_COPYSET (&InputThreadFd, &InputDevices); + i = Select(MaxInputDevices, &InputDevices, NULL, NULL, NULL); + DebugF("threading input generation)\n"); + if (i <= 0) /* An error or timeout occurred */ + { + if (i < 0) /* This logic are stolen from WaitFor.c */ + { + if (errno == EINVAL) + { + FatalError("WaitForInput(): select: %s\n", + strerror(errno)); + } + else if (errno != EINTR && errno != EAGAIN) + { + ErrorF("WaitForInput(): select: %s\n", + strerror(errno)); + } + } + } + else + { + for (i = 0; i <= NumDevicesThreaded; i++) + if ((FD_ISSET(ThreadDevFunc[i].fd, &InputThreadFd)) + && ThreadDevFunc[i].f) + (*ThreadDevFunc[i].f)(ThreadDevFunc[i].closure); + + } + + /* Send a null byte to the main thread so it wakes select up to + * process the events in mieqProcessInputEvents() */ + PipeWrite(InputThreadWritePipe); + } +} + +/* + * Called by the dix to create the input thread. + */ +void +CreateInputThread (void) +{ + pthread_t input_thread; + pthread_attr_t attr; + + pthread_attr_init(&attr); + + /* In OSes that differentiate processes and threads this bellow may have + * some sense. Linux uses 1:1 thread model. The scheduler handles every + * thread as a normal process. Therefore this probably have no meaning + * if you are under Linux. + */ + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0) + perror("error setting input thread scope"); + + if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) + perror("error setting input thread stack size"); + + pthread_create(&input_thread, &attr, (void *)&WaitForInput, NULL); + pthread_attr_destroy (&attr); +} + +/* + * Called by the dix to close the input thread cleanly. + */ +void +CloseInputThread (void) +{ + RemoveEnabledDevice(InputThreadReadPipe); + FD_ZERO(&InputThreadFd); + NumDevicesThreaded = 0; + + syscall(__NR_tkill, tid_generation); +} |