os: Use ospoll for input thread [v2]

Replace use of select(2) to avoid fd limits. Note that
InputThreadFillPipe used select as well, but none of the files passed
were non-blocking, so there was no need for that code at all.

v2: Keep ospoll API usage single threaded to avoid re-entrancy issues

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
This commit is contained in:
Keith Packard 2016-05-24 21:12:33 -07:00 committed by Adam Jackson
parent f993091e7d
commit 30bc0732f9

View File

@ -35,7 +35,6 @@
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <X11/Xpoll.h>
#include "inputstr.h" #include "inputstr.h"
#include "opaque.h" #include "opaque.h"
#include "osdep.h" #include "osdep.h"
@ -47,11 +46,19 @@ Bool InputThreadEnable = TRUE;
/** /**
* An input device as seen by the threaded input facility * An input device as seen by the threaded input facility
*/ */
typedef enum _InputDeviceState {
device_state_added,
device_state_running,
device_state_removed
} InputDeviceState;
typedef struct _InputThreadDevice { typedef struct _InputThreadDevice {
struct xorg_list node; struct xorg_list node;
NotifyFdProcPtr readInputProc; NotifyFdProcPtr readInputProc;
void *readInputArgs; void *readInputArgs;
int fd; int fd;
InputDeviceState state;
} InputThreadDevice; } InputThreadDevice;
/** /**
@ -62,9 +69,11 @@ typedef struct _InputThreadDevice {
typedef struct { typedef struct {
pthread_t thread; pthread_t thread;
struct xorg_list devs; struct xorg_list devs;
fd_set fds; struct ospoll *fds;
int readPipe; int readPipe;
int writePipe; int writePipe;
Bool changed;
Bool running;
} InputThreadInfo; } InputThreadInfo;
static InputThreadInfo *inputThreadInfo; static InputThreadInfo *inputThreadInfo;
@ -154,6 +163,17 @@ InputThreadReadPipe(int readHead)
return 1; return 1;
} }
static void
InputReady(int fd, int xevents, void *data)
{
InputThreadDevice *dev = data;
input_lock();
if (dev->state == device_state_running)
dev->readInputProc(fd, xevents, dev->readInputArgs);
input_unlock();
}
/** /**
* Register an input device in the threaded input facility * Register an input device in the threaded input facility
* *
@ -182,16 +202,18 @@ InputThreadRegisterDev(int fd,
dev->fd = fd; dev->fd = fd;
dev->readInputProc = readInputProc; dev->readInputProc = readInputProc;
dev->readInputArgs = readInputArgs; dev->readInputArgs = readInputArgs;
dev->state = device_state_added;
input_lock(); input_lock();
xorg_list_add(&dev->node, &inputThreadInfo->devs); xorg_list_add(&dev->node, &inputThreadInfo->devs);
FD_SET(fd, &inputThreadInfo->fds); inputThreadInfo->changed = TRUE;
InputThreadFillPipe(hotplugPipeWrite);
DebugF("input-thread: registered device %d\n", fd);
input_unlock(); input_unlock();
DebugF("input-thread: registered device %d\n", fd);
InputThreadFillPipe(hotplugPipeWrite);
return 1; return 1;
} }
@ -229,20 +251,26 @@ InputThreadUnregisterDev(int fd)
return 0; return 0;
} }
xorg_list_del(&dev->node); dev->state = device_state_removed;
inputThreadInfo->changed = TRUE;
FD_CLR(fd, &inputThreadInfo->fds);
input_unlock(); input_unlock();
free(dev);
InputThreadFillPipe(hotplugPipeWrite); InputThreadFillPipe(hotplugPipeWrite);
DebugF("input-thread: unregistered device: %d\n", fd); DebugF("input-thread: unregistered device: %d\n", fd);
return 1; return 1;
} }
static void
InputThreadPipeNotify(int fd, int revents, void *data)
{
/* Empty pending input, shut down if the pipe has been closed */
if (InputThreadReadPipe(hotplugPipeRead) == 0) {
inputThreadInfo->running = FALSE;
}
}
/** /**
* The workhorse of threaded input event generation. * The workhorse of threaded input event generation.
* *
@ -260,51 +288,66 @@ InputThreadUnregisterDev(int fd)
static void* static void*
InputThreadDoWork(void *arg) InputThreadDoWork(void *arg)
{ {
fd_set readyFds;
InputThreadDevice *dev, *next;
sigset_t set; sigset_t set;
/* Don't handle any signals on this thread */ /* Don't handle any signals on this thread */
sigfillset(&set); sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, NULL); pthread_sigmask(SIG_BLOCK, &set, NULL);
FD_ZERO(&readyFds); inputThreadInfo->running = TRUE;
while (1) ospoll_add(inputThreadInfo->fds, hotplugPipeRead,
ospoll_trigger_level,
InputThreadPipeNotify,
NULL);
ospoll_listen(inputThreadInfo->fds, hotplugPipeRead, X_NOTIFY_READ);
while (inputThreadInfo->running)
{ {
XFD_COPYSET(&inputThreadInfo->fds, &readyFds);
FD_SET(hotplugPipeRead, &readyFds);
DebugF("input-thread: %s waiting for devices\n", __func__); DebugF("input-thread: %s waiting for devices\n", __func__);
if (Select(MAXSELECT, &readyFds, NULL, NULL, NULL) < 0) { /* Check for hotplug changes and modify the ospoll structure to suit */
if (inputThreadInfo->changed) {
InputThreadDevice *dev, *tmp;
input_lock();
inputThreadInfo->changed = FALSE;
xorg_list_for_each_entry_safe(dev, tmp, &inputThreadInfo->devs, node) {
switch (dev->state) {
case device_state_added:
ospoll_add(inputThreadInfo->fds, dev->fd,
ospoll_trigger_level,
InputReady,
dev);
ospoll_listen(inputThreadInfo->fds, dev->fd, X_NOTIFY_READ);
dev->state = device_state_running;
break;
case device_state_running:
break;
case device_state_removed:
ospoll_remove(inputThreadInfo->fds, dev->fd);
xorg_list_del(&dev->node);
free(dev);
break;
}
}
input_unlock();
}
if (ospoll_wait(inputThreadInfo->fds, -1) < 0) {
if (errno == EINVAL) if (errno == EINVAL)
FatalError("input-thread: %s (%s)", __func__, strerror(errno)); FatalError("input-thread: %s (%s)", __func__, strerror(errno));
else if (errno != EINTR) else if (errno != EINTR)
ErrorF("input-thread: %s (%s)\n", __func__, strerror(errno)); ErrorF("input-thread: %s (%s)\n", __func__, strerror(errno));
} }
DebugF("input-thread: %s generating events\n", __func__);
input_lock();
/* Call the device drivers to generate input events for us */
xorg_list_for_each_entry_safe(dev, next, &inputThreadInfo->devs, node) {
if (FD_ISSET(dev->fd, &readyFds) && dev->readInputProc) {
dev->readInputProc(dev->fd, X_NOTIFY_READ, dev->readInputArgs);
}
}
input_unlock();
/* Kick main thread to process the generated input events and drain /* Kick main thread to process the generated input events and drain
* events from hotplug pipe */ * events from hotplug pipe */
InputThreadFillPipe(inputThreadInfo->writePipe); InputThreadFillPipe(inputThreadInfo->writePipe);
/* Empty pending input, shut down if the pipe has been closed */
if (FD_ISSET(hotplugPipeRead, &readyFds)) {
if (InputThreadReadPipe(hotplugPipeRead) == 0)
break;
}
} }
ospoll_remove(inputThreadInfo->fds, hotplugPipeRead);
return NULL; return NULL;
} }
@ -338,7 +381,7 @@ InputThreadPreInit(void)
inputThreadInfo->thread = 0; inputThreadInfo->thread = 0;
xorg_list_init(&inputThreadInfo->devs); xorg_list_init(&inputThreadInfo->devs);
FD_ZERO(&inputThreadInfo->fds); inputThreadInfo->fds = ospoll_create();
/* By making read head non-blocking, we ensure that while the main thread /* By making read head non-blocking, we ensure that while the main thread
* is busy servicing client requests, the dedicated input thread can work * is busy servicing client requests, the dedicated input thread can work
@ -353,6 +396,7 @@ InputThreadPreInit(void)
hotplugPipeRead = hotplugPipe[0]; hotplugPipeRead = hotplugPipe[0];
fcntl(hotplugPipeRead, F_SETFL, O_NONBLOCK | O_CLOEXEC); fcntl(hotplugPipeRead, F_SETFL, O_NONBLOCK | O_CLOEXEC);
hotplugPipeWrite = hotplugPipe[1]; hotplugPipeWrite = hotplugPipe[1];
} }
/** /**
@ -407,11 +451,11 @@ InputThreadFini(void)
pthread_join(inputThreadInfo->thread, NULL); pthread_join(inputThreadInfo->thread, NULL);
xorg_list_for_each_entry_safe(dev, next, &inputThreadInfo->devs, node) { xorg_list_for_each_entry_safe(dev, next, &inputThreadInfo->devs, node) {
FD_CLR(dev->fd, &inputThreadInfo->fds); ospoll_remove(inputThreadInfo->fds, dev->fd);
free(dev); free(dev);
} }
xorg_list_init(&inputThreadInfo->devs); xorg_list_init(&inputThreadInfo->devs);
FD_ZERO(&inputThreadInfo->fds); ospoll_destroy(inputThreadInfo->fds);
RemoveNotifyFd(inputThreadInfo->readPipe); RemoveNotifyFd(inputThreadInfo->readPipe);
close(inputThreadInfo->readPipe); close(inputThreadInfo->readPipe);