inputthread.c (15182B)
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 94 in_input_thread(void) 95 { 96 return inputThreadInfo && 97 pthread_equal(pthread_self(), inputThreadInfo->thread); 98 } 99 100 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 118 input_unlock(void) 119 { 120 --input_mutex_count; 121 pthread_mutex_unlock(&input_mutex); 122 } 123 124 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 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 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 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 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 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 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* 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 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 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 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 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 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 537 void input_lock(void) {} 538 void input_unlock(void) {} 539 void input_force_unlock(void) {} 540 541 void InputThreadPreInit(void) {} 542 void InputThreadInit(void) {} 543 void InputThreadFini(void) {} 544 int in_input_thread(void) { return 0; } 545 546 int InputThreadRegisterDev(int fd, 547 NotifyFdProcPtr readInputProc, 548 void *readInputArgs) 549 { 550 return SetNotifyFd(fd, readInputProc, X_NOTIFY_READ, readInputArgs); 551 } 552 553 extern int InputThreadUnregisterDev(int fd) 554 { 555 RemoveNotifyFd(fd); 556 return 1; 557 } 558 559 int xthread_sigmask(int how, const sigset_t *set, sigset_t *oldset) 560 { 561 #ifdef HAVE_SIGPROCMASK 562 return sigprocmask(how, set, oldset); 563 #else 564 return 0; 565 #endif 566 } 567 568 #endif