xref: /OK3568_Linux_fs/external/xserver/os/inputthread.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /* inputthread.c -- Threaded generation of input events.
2  *
3  * Copyright © 2007-2008 Tiago Vignatti <vignatti at freedesktop org>
4  * Copyright © 2010 Nokia
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors: Fernando Carrijo <fcarrijo at freedesktop org>
25  *          Tiago Vignatti <vignatti at freedesktop org>
26  */
27 
28 #ifdef HAVE_DIX_CONFIG_H
29 #include <dix-config.h>
30 #endif
31 
32 #include <stdio.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <pthread.h>
37 
38 #include "inputstr.h"
39 #include "opaque.h"
40 #include "osdep.h"
41 
42 #if INPUTTHREAD
43 
44 Bool InputThreadEnable = TRUE;
45 
46 /**
47  * An input device as seen by the threaded input facility
48  */
49 
50 typedef enum _InputDeviceState {
51     device_state_added,
52     device_state_running,
53     device_state_removed
54 } InputDeviceState;
55 
56 typedef struct _InputThreadDevice {
57     struct xorg_list node;
58     NotifyFdProcPtr readInputProc;
59     void *readInputArgs;
60     int fd;
61     InputDeviceState state;
62 } InputThreadDevice;
63 
64 /**
65  * The threaded input facility.
66  *
67  * For now, we have one instance for all input devices.
68  */
69 typedef struct {
70     pthread_t thread;
71     struct xorg_list devs;
72     struct ospoll *fds;
73     int readPipe;
74     int writePipe;
75     Bool changed;
76     Bool running;
77 } InputThreadInfo;
78 
79 static InputThreadInfo *inputThreadInfo;
80 
81 static int hotplugPipeRead = -1;
82 static int hotplugPipeWrite = -1;
83 
84 static int input_mutex_count;
85 
86 #ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
87 static pthread_mutex_t input_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
88 #else
89 static pthread_mutex_t input_mutex;
90 static Bool input_mutex_initialized;
91 #endif
92 
93 int
in_input_thread(void)94 in_input_thread(void)
95 {
96     return inputThreadInfo &&
97            pthread_equal(pthread_self(), inputThreadInfo->thread);
98 }
99 
100 void
input_lock(void)101 input_lock(void)
102 {
103 #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
104     if (!input_mutex_initialized) {
105         pthread_mutexattr_t mutex_attr;
106 
107         input_mutex_initialized = TRUE;
108         pthread_mutexattr_init(&mutex_attr);
109         pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
110         pthread_mutex_init(&input_mutex, &mutex_attr);
111     }
112 #endif
113     pthread_mutex_lock(&input_mutex);
114     ++input_mutex_count;
115 }
116 
117 void
input_unlock(void)118 input_unlock(void)
119 {
120     --input_mutex_count;
121     pthread_mutex_unlock(&input_mutex);
122 }
123 
124 void
input_force_unlock(void)125 input_force_unlock(void)
126 {
127     if (pthread_mutex_trylock(&input_mutex) == 0) {
128         input_mutex_count++;
129         /* unlock +1 times for the trylock */
130         while (input_mutex_count > 0)
131             input_unlock();
132     }
133 }
134 
135 /**
136  * Notify a thread about the availability of new asynchronously enqueued input
137  * events.
138  *
139  * @see WaitForSomething()
140  */
141 static void
InputThreadFillPipe(int writeHead)142 InputThreadFillPipe(int writeHead)
143 {
144     int ret;
145     char byte = 0;
146 
147     do {
148         ret = write(writeHead, &byte, 1);
149     } while (ret < 0 && ETEST(errno));
150 }
151 
152 /**
153  * Consume eventual notifications left by a thread.
154  *
155  * @see WaitForSomething()
156  * @see InputThreadFillPipe()
157  */
158 static int
InputThreadReadPipe(int readHead)159 InputThreadReadPipe(int readHead)
160 {
161     int ret, array[10];
162 
163     ret = read(readHead, &array, sizeof(array));
164     if (ret >= 0)
165         return ret;
166 
167     if (errno != EAGAIN)
168         FatalError("input-thread: draining pipe (%d)", errno);
169 
170     return 1;
171 }
172 
173 static void
InputReady(int fd,int xevents,void * data)174 InputReady(int fd, int xevents, void *data)
175 {
176     InputThreadDevice *dev = data;
177 
178     input_lock();
179     if (dev->state == device_state_running)
180         dev->readInputProc(fd, xevents, dev->readInputArgs);
181     input_unlock();
182 }
183 
184 /**
185  * Register an input device in the threaded input facility
186  *
187  * @param fd File descriptor which identifies the input device
188  * @param readInputProc Procedure used to read input from the device
189  * @param readInputArgs Arguments to be consumed by the above procedure
190  *
191  * return 1 if success; 0 otherwise.
192  */
193 int
InputThreadRegisterDev(int fd,NotifyFdProcPtr readInputProc,void * readInputArgs)194 InputThreadRegisterDev(int fd,
195                        NotifyFdProcPtr readInputProc,
196                        void *readInputArgs)
197 {
198     InputThreadDevice *dev, *old;
199 
200     if (!inputThreadInfo)
201         return SetNotifyFd(fd, readInputProc, X_NOTIFY_READ, readInputArgs);
202 
203     input_lock();
204 
205     dev = NULL;
206     xorg_list_for_each_entry(old, &inputThreadInfo->devs, node) {
207         if (old->fd == fd && old->state != device_state_removed) {
208             dev = old;
209             break;
210         }
211     }
212 
213     if (dev) {
214         dev->readInputProc = readInputProc;
215         dev->readInputArgs = readInputArgs;
216     } else {
217         dev = calloc(1, sizeof(InputThreadDevice));
218         if (dev == NULL) {
219             DebugF("input-thread: could not register device\n");
220             input_unlock();
221             return 0;
222         }
223 
224         dev->fd = fd;
225         dev->readInputProc = readInputProc;
226         dev->readInputArgs = readInputArgs;
227         dev->state = device_state_added;
228 
229         /* Do not prepend, so that any dev->state == device_state_removed
230          * with the same dev->fd get processed first. */
231         xorg_list_append(&dev->node, &inputThreadInfo->devs);
232     }
233 
234     inputThreadInfo->changed = TRUE;
235 
236     input_unlock();
237 
238     DebugF("input-thread: registered device %d\n", fd);
239     InputThreadFillPipe(hotplugPipeWrite);
240 
241     return 1;
242 }
243 
244 /**
245  * Unregister a device in the threaded input facility
246  *
247  * @param fd File descriptor which identifies the input device
248  *
249  * @return 1 if success; 0 otherwise.
250  */
251 int
InputThreadUnregisterDev(int fd)252 InputThreadUnregisterDev(int fd)
253 {
254     InputThreadDevice *dev;
255     Bool found_device = FALSE;
256 
257     /* return silently if input thread is already finished (e.g., at
258      * DisableDevice time, evdev tries to call this function again through
259      * xf86RemoveEnabledDevice) */
260     if (!inputThreadInfo) {
261         RemoveNotifyFd(fd);
262         return 1;
263     }
264 
265     input_lock();
266     xorg_list_for_each_entry(dev, &inputThreadInfo->devs, node)
267         if (dev->fd == fd) {
268             found_device = TRUE;
269             break;
270         }
271 
272     /* fd didn't match any registered device. */
273     if (!found_device) {
274         input_unlock();
275         return 0;
276     }
277 
278     dev->state = device_state_removed;
279     inputThreadInfo->changed = TRUE;
280 
281     input_unlock();
282 
283     InputThreadFillPipe(hotplugPipeWrite);
284     DebugF("input-thread: unregistered device: %d\n", fd);
285 
286     return 1;
287 }
288 
289 static void
InputThreadPipeNotify(int fd,int revents,void * data)290 InputThreadPipeNotify(int fd, int revents, void *data)
291 {
292     /* Empty pending input, shut down if the pipe has been closed */
293     if (InputThreadReadPipe(hotplugPipeRead) == 0) {
294         inputThreadInfo->running = FALSE;
295     }
296 }
297 
298 /**
299  * The workhorse of threaded input event generation.
300  *
301  * Or if you prefer: The WaitForSomething for input devices. :)
302  *
303  * Runs in parallel with the server main thread, listening to input devices in
304  * an endless loop. Whenever new input data is made available, calls the
305  * proper device driver's routines which are ultimately responsible for the
306  * generation of input events.
307  *
308  * @see InputThreadPreInit()
309  * @see InputThreadInit()
310  */
311 
312 static void*
InputThreadDoWork(void * arg)313 InputThreadDoWork(void *arg)
314 {
315     sigset_t set;
316 
317     /* Don't handle any signals on this thread */
318     sigfillset(&set);
319     pthread_sigmask(SIG_BLOCK, &set, NULL);
320 
321     ddxInputThreadInit();
322 
323     inputThreadInfo->running = TRUE;
324 
325 #if defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
326     pthread_setname_np (pthread_self(), "InputThread");
327 #elif defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
328     pthread_setname_np ("InputThread");
329 #endif
330 
331     ospoll_add(inputThreadInfo->fds, hotplugPipeRead,
332                ospoll_trigger_level,
333                InputThreadPipeNotify,
334                NULL);
335     ospoll_listen(inputThreadInfo->fds, hotplugPipeRead, X_NOTIFY_READ);
336 
337     while (inputThreadInfo->running)
338     {
339         DebugF("input-thread: %s waiting for devices\n", __func__);
340 
341         /* Check for hotplug changes and modify the ospoll structure to suit */
342         if (inputThreadInfo->changed) {
343             InputThreadDevice *dev, *tmp;
344 
345             input_lock();
346             inputThreadInfo->changed = FALSE;
347             xorg_list_for_each_entry_safe(dev, tmp, &inputThreadInfo->devs, node) {
348                 switch (dev->state) {
349                 case device_state_added:
350                     ospoll_add(inputThreadInfo->fds, dev->fd,
351                                ospoll_trigger_level,
352                                InputReady,
353                                dev);
354                     ospoll_listen(inputThreadInfo->fds, dev->fd, X_NOTIFY_READ);
355                     dev->state = device_state_running;
356                     break;
357                 case device_state_running:
358                     break;
359                 case device_state_removed:
360                     ospoll_remove(inputThreadInfo->fds, dev->fd);
361                     xorg_list_del(&dev->node);
362                     free(dev);
363                     break;
364                 }
365             }
366             input_unlock();
367         }
368 
369         if (ospoll_wait(inputThreadInfo->fds, -1) < 0) {
370             if (errno == EINVAL)
371                 FatalError("input-thread: %s (%s)", __func__, strerror(errno));
372             else if (errno != EINTR)
373                 ErrorF("input-thread: %s (%s)\n", __func__, strerror(errno));
374         }
375 
376         /* Kick main thread to process the generated input events and drain
377          * events from hotplug pipe */
378         InputThreadFillPipe(inputThreadInfo->writePipe);
379     }
380 
381     ospoll_remove(inputThreadInfo->fds, hotplugPipeRead);
382 
383     return NULL;
384 }
385 
386 static void
InputThreadNotifyPipe(int fd,int mask,void * data)387 InputThreadNotifyPipe(int fd, int mask, void *data)
388 {
389     InputThreadReadPipe(fd);
390 }
391 
392 /**
393  * Pre-initialize the facility used for threaded generation of input events
394  *
395  */
396 void
InputThreadPreInit(void)397 InputThreadPreInit(void)
398 {
399     int fds[2], hotplugPipe[2];
400     int flags;
401 
402     if (!InputThreadEnable)
403         return;
404 
405     if (pipe(fds) < 0)
406         FatalError("input-thread: could not create pipe");
407 
408      if (pipe(hotplugPipe) < 0)
409         FatalError("input-thread: could not create pipe");
410 
411     inputThreadInfo = malloc(sizeof(InputThreadInfo));
412     if (!inputThreadInfo)
413         FatalError("input-thread: could not allocate memory");
414 
415     inputThreadInfo->changed = FALSE;
416 
417     inputThreadInfo->thread = 0;
418     xorg_list_init(&inputThreadInfo->devs);
419     inputThreadInfo->fds = ospoll_create();
420 
421     /* By making read head non-blocking, we ensure that while the main thread
422      * is busy servicing client requests, the dedicated input thread can work
423      * in parallel.
424      */
425     inputThreadInfo->readPipe = fds[0];
426     fcntl(inputThreadInfo->readPipe, F_SETFL, O_NONBLOCK);
427     flags = fcntl(inputThreadInfo->readPipe, F_GETFD);
428     if (flags != -1) {
429         flags |= FD_CLOEXEC;
430         (void)fcntl(inputThreadInfo->readPipe, F_SETFD, &flags);
431     }
432     SetNotifyFd(inputThreadInfo->readPipe, InputThreadNotifyPipe, X_NOTIFY_READ, NULL);
433 
434     inputThreadInfo->writePipe = fds[1];
435 
436     hotplugPipeRead = hotplugPipe[0];
437     fcntl(hotplugPipeRead, F_SETFL, O_NONBLOCK);
438     flags = fcntl(hotplugPipeRead, F_GETFD);
439     if (flags != -1) {
440         flags |= FD_CLOEXEC;
441         (void)fcntl(hotplugPipeRead, F_SETFD, &flags);
442     }
443     hotplugPipeWrite = hotplugPipe[1];
444 
445 #ifndef __linux__ /* Linux does not deal well with renaming the main thread */
446 #if defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
447     pthread_setname_np (pthread_self(), "MainThread");
448 #elif defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
449     pthread_setname_np ("MainThread");
450 #endif
451 #endif
452 
453 }
454 
455 /**
456  * Start the threaded generation of input events. This routine complements what
457  * was previously done by InputThreadPreInit(), being only responsible for
458  * creating the dedicated input thread.
459  *
460  */
461 void
InputThreadInit(void)462 InputThreadInit(void)
463 {
464     pthread_attr_t attr;
465 
466     /* If the driver hasn't asked for input thread support by calling
467      * InputThreadPreInit, then do nothing here
468      */
469     if (!inputThreadInfo)
470         return;
471 
472     pthread_attr_init(&attr);
473 
474     /* For OSes that differentiate between processes and threads, the following
475      * lines have sense. Linux uses the 1:1 thread model. The scheduler handles
476      * every thread as a normal process. Therefore this probably has no meaning
477      * if we are under Linux.
478      */
479     if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0)
480         ErrorF("input-thread: error setting thread scope\n");
481 
482     DebugF("input-thread: creating thread\n");
483     pthread_create(&inputThreadInfo->thread, &attr,
484                    &InputThreadDoWork, NULL);
485 
486     pthread_attr_destroy (&attr);
487 }
488 
489 /**
490  * Stop the threaded generation of input events
491  *
492  * This function is supposed to be called at server shutdown time only.
493  */
494 void
InputThreadFini(void)495 InputThreadFini(void)
496 {
497     InputThreadDevice *dev, *next;
498 
499     if (!inputThreadInfo)
500         return;
501 
502     /* Close the pipe to get the input thread to shut down */
503     close(hotplugPipeWrite);
504     input_force_unlock();
505     pthread_join(inputThreadInfo->thread, NULL);
506 
507     xorg_list_for_each_entry_safe(dev, next, &inputThreadInfo->devs, node) {
508         ospoll_remove(inputThreadInfo->fds, dev->fd);
509         free(dev);
510     }
511     xorg_list_init(&inputThreadInfo->devs);
512     ospoll_destroy(inputThreadInfo->fds);
513 
514     RemoveNotifyFd(inputThreadInfo->readPipe);
515     close(inputThreadInfo->readPipe);
516     close(inputThreadInfo->writePipe);
517     inputThreadInfo->readPipe = -1;
518     inputThreadInfo->writePipe = -1;
519 
520     close(hotplugPipeRead);
521     hotplugPipeRead = -1;
522     hotplugPipeWrite = -1;
523 
524     free(inputThreadInfo);
525     inputThreadInfo = NULL;
526 }
527 
xthread_sigmask(int how,const sigset_t * set,sigset_t * oldset)528 int xthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
529 {
530     return pthread_sigmask(how, set, oldset);
531 }
532 
533 #else /* INPUTTHREAD */
534 
535 Bool InputThreadEnable = FALSE;
536 
input_lock(void)537 void input_lock(void) {}
input_unlock(void)538 void input_unlock(void) {}
input_force_unlock(void)539 void input_force_unlock(void) {}
540 
InputThreadPreInit(void)541 void InputThreadPreInit(void) {}
InputThreadInit(void)542 void InputThreadInit(void) {}
InputThreadFini(void)543 void InputThreadFini(void) {}
in_input_thread(void)544 int in_input_thread(void) { return 0; }
545 
InputThreadRegisterDev(int fd,NotifyFdProcPtr readInputProc,void * readInputArgs)546 int InputThreadRegisterDev(int fd,
547                            NotifyFdProcPtr readInputProc,
548                            void *readInputArgs)
549 {
550     return SetNotifyFd(fd, readInputProc, X_NOTIFY_READ, readInputArgs);
551 }
552 
InputThreadUnregisterDev(int fd)553 extern int InputThreadUnregisterDev(int fd)
554 {
555     RemoveNotifyFd(fd);
556     return 1;
557 }
558 
xthread_sigmask(int how,const sigset_t * set,sigset_t * oldset)559 int xthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
560 {
561     return sigprocmask(how, set, oldset);
562 }
563 
564 #endif
565