xwayland: Add xdg-system-bell support
[xserver.git] / os / ospoll.c
blobdc2ecd10fc86d4cb2962df713f6c21fa9bc73bd4
1 /*
2 * Copyright © 2016 Keith Packard
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
23 #include <dix-config.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <X11/X.h>
28 #include <X11/Xproto.h>
30 #include "os/xserver_poll.h"
32 #include "misc.h" /* for typedef of pointer */
33 #include "ospoll.h"
34 #include "list.h"
36 #if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE)
37 #include <sys/pollset.h>
38 #define POLLSET 1
39 #define HAVE_OSPOLL 1
40 #endif
42 #if !HAVE_OSPOLL && defined(HAVE_PORT_CREATE)
43 #include <port.h>
44 #include <poll.h>
45 #define PORT 1
46 #define HAVE_OSPOLL 1
47 #endif
49 #if !HAVE_OSPOLL && defined(HAVE_EPOLL_CREATE1)
50 #include <sys/epoll.h>
51 #define EPOLL 1
52 #define HAVE_OSPOLL 1
53 #endif
55 #if !HAVE_OSPOLL
56 #include "xserver_poll.h"
57 #define POLL 1
58 #define HAVE_OSPOLL 1
59 #endif
61 #if POLLSET
63 // pollset-based implementation (as seen on AIX)
64 struct ospollfd {
65 int fd;
66 int xevents;
67 short revents;
68 enum ospoll_trigger trigger;
69 void (*callback)(int fd, int xevents, void *data);
70 void *data;
73 struct ospoll {
74 pollset_t ps;
75 struct ospollfd *fds;
76 int num;
77 int size;
80 #endif
82 #if EPOLL || PORT
84 /* epoll-based implementation */
85 struct ospollfd {
86 int fd;
87 int xevents;
88 enum ospoll_trigger trigger;
89 void (*callback)(int fd, int xevents, void *data);
90 void *data;
91 struct xorg_list deleted;
94 struct ospoll {
95 int epoll_fd;
96 struct ospollfd **fds;
97 int num;
98 int size;
99 struct xorg_list deleted;
102 #endif
104 #if POLL
106 /* poll-based implementation */
107 struct ospollfd {
108 short revents;
109 enum ospoll_trigger trigger;
110 void (*callback)(int fd, int revents, void *data);
111 void *data;
114 struct ospoll {
115 struct pollfd *fds;
116 struct ospollfd *osfds;
117 int num;
118 int size;
119 Bool changed;
122 #endif
124 /* Binary search for the specified file descriptor
126 * Returns position if found
127 * Returns -position - 1 if not found
130 static int
131 ospoll_find(struct ospoll *ospoll, int fd)
133 int lo = 0;
134 int hi = ospoll->num - 1;
136 while (lo <= hi) {
137 int m = (lo + hi) >> 1;
138 #if EPOLL || PORT
139 int t = ospoll->fds[m]->fd;
140 #endif
141 #if POLL || POLLSET
142 int t = ospoll->fds[m].fd;
143 #endif
145 if (t < fd)
146 lo = m + 1;
147 else if (t > fd)
148 hi = m - 1;
149 else
150 return m;
152 return -(lo + 1);
155 #if EPOLL || PORT
156 static void
157 ospoll_clean_deleted(struct ospoll *ospoll)
159 struct ospollfd *osfd, *tmp;
161 xorg_list_for_each_entry_safe(osfd, tmp, &ospoll->deleted, deleted) {
162 xorg_list_del(&osfd->deleted);
163 free(osfd);
166 #endif
168 /* Insert an element into an array
170 * base: base address of array
171 * num: number of elements in the array before the insert
172 * size: size of each element
173 * pos: position to insert at
175 static inline void
176 array_insert(void *base, size_t num, size_t size, size_t pos)
178 char *b = base;
180 memmove(b + (pos+1) * size,
181 b + pos * size,
182 (num - pos) * size);
185 /* Delete an element from an array
187 * base: base address of array
188 * num: number of elements in the array before the delete
189 * size: size of each element
190 * pos: position to delete from
192 static inline void
193 array_delete(void *base, size_t num, size_t size, size_t pos)
195 char *b = base;
197 memmove(b + pos * size, b + (pos + 1) * size,
198 (num - pos - 1) * size);
202 struct ospoll *
203 ospoll_create(void)
205 #if POLLSET
206 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
208 ospoll->ps = pollset_create(-1);
209 if (ospoll->ps < 0) {
210 free (ospoll);
211 return NULL;
213 return ospoll;
214 #endif
215 #if PORT
216 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
218 ospoll->epoll_fd = port_create();
219 if (ospoll->epoll_fd < 0) {
220 free (ospoll);
221 return NULL;
223 xorg_list_init(&ospoll->deleted);
224 return ospoll;
225 #endif
226 #if EPOLL
227 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
229 ospoll->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
230 if (ospoll->epoll_fd < 0) {
231 free (ospoll);
232 return NULL;
234 xorg_list_init(&ospoll->deleted);
235 return ospoll;
236 #endif
237 #if POLL
238 return calloc(1, sizeof (struct ospoll));
239 #endif
242 void
243 ospoll_destroy(struct ospoll *ospoll)
245 #if POLLSET
246 if (ospoll) {
247 assert (ospoll->num == 0);
248 pollset_destroy(ospoll->ps);
249 free(ospoll->fds);
250 free(ospoll);
252 #endif
253 #if EPOLL || PORT
254 if (ospoll) {
255 assert (ospoll->num == 0);
256 close(ospoll->epoll_fd);
257 ospoll_clean_deleted(ospoll);
258 free(ospoll->fds);
259 free(ospoll);
261 #endif
262 #if POLL
263 if (ospoll) {
264 assert (ospoll->num == 0);
265 free (ospoll->fds);
266 free (ospoll->osfds);
267 free (ospoll);
269 #endif
272 Bool
273 ospoll_add(struct ospoll *ospoll, int fd,
274 enum ospoll_trigger trigger,
275 void (*callback)(int fd, int xevents, void *data),
276 void *data)
278 int pos = ospoll_find(ospoll, fd);
279 #if POLLSET
280 if (pos < 0) {
281 if (ospoll->num == ospoll->size) {
282 struct ospollfd *new_fds;
283 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
285 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
286 if (!new_fds)
287 return FALSE;
288 ospoll->fds = new_fds;
289 ospoll->size = new_size;
291 pos = -pos - 1;
292 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
293 ospoll->num++;
295 ospoll->fds[pos].fd = fd;
296 ospoll->fds[pos].xevents = 0;
297 ospoll->fds[pos].revents = 0;
299 ospoll->fds[pos].trigger = trigger;
300 ospoll->fds[pos].callback = callback;
301 ospoll->fds[pos].data = data;
302 #endif
303 #if PORT
304 struct ospollfd *osfd;
306 if (pos < 0) {
307 osfd = calloc(1, sizeof (struct ospollfd));
308 if (!osfd)
309 return FALSE;
311 if (ospoll->num >= ospoll->size) {
312 struct ospollfd **new_fds;
313 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
315 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
316 if (!new_fds) {
317 free (osfd);
318 return FALSE;
320 ospoll->fds = new_fds;
321 ospoll->size = new_size;
324 osfd->fd = fd;
325 osfd->xevents = 0;
327 pos = -pos - 1;
328 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
329 ospoll->fds[pos] = osfd;
330 ospoll->num++;
331 } else {
332 osfd = ospoll->fds[pos];
334 osfd->data = data;
335 osfd->callback = callback;
336 osfd->trigger = trigger;
337 #endif
338 #if EPOLL
339 struct ospollfd *osfd;
341 if (pos < 0) {
343 struct epoll_event ev;
345 osfd = calloc(1, sizeof (struct ospollfd));
346 if (!osfd)
347 return FALSE;
349 if (ospoll->num >= ospoll->size) {
350 struct ospollfd **new_fds;
351 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
353 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
354 if (!new_fds) {
355 free (osfd);
356 return FALSE;
358 ospoll->fds = new_fds;
359 ospoll->size = new_size;
362 ev.events = 0;
363 ev.data.ptr = osfd;
364 if (trigger == ospoll_trigger_edge)
365 ev.events |= EPOLLET;
366 if (epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
367 free(osfd);
368 return FALSE;
370 osfd->fd = fd;
371 osfd->xevents = 0;
373 pos = -pos - 1;
374 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
375 ospoll->fds[pos] = osfd;
376 ospoll->num++;
377 } else {
378 osfd = ospoll->fds[pos];
380 osfd->data = data;
381 osfd->callback = callback;
382 osfd->trigger = trigger;
383 #endif
384 #if POLL
385 if (pos < 0) {
386 if (ospoll->num == ospoll->size) {
387 struct pollfd *new_fds;
388 struct ospollfd *new_osfds;
389 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
391 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
392 if (!new_fds)
393 return FALSE;
394 ospoll->fds = new_fds;
395 new_osfds = reallocarray(ospoll->osfds, new_size, sizeof (ospoll->osfds[0]));
396 if (!new_osfds)
397 return FALSE;
398 ospoll->osfds = new_osfds;
399 ospoll->size = new_size;
401 pos = -pos - 1;
402 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
403 array_insert(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
404 ospoll->num++;
405 ospoll->changed = TRUE;
407 ospoll->fds[pos].fd = fd;
408 ospoll->fds[pos].events = 0;
409 ospoll->fds[pos].revents = 0;
410 ospoll->osfds[pos].revents = 0;
412 ospoll->osfds[pos].trigger = trigger;
413 ospoll->osfds[pos].callback = callback;
414 ospoll->osfds[pos].data = data;
415 #endif
416 return TRUE;
419 void
420 ospoll_remove(struct ospoll *ospoll, int fd)
422 int pos = ospoll_find(ospoll, fd);
424 pos = ospoll_find(ospoll, fd);
425 if (pos >= 0) {
426 #if POLLSET
427 struct ospollfd *osfd = &ospoll->fds[pos];
428 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
429 pollset_ctl(ospoll->ps, &ctl, 1);
431 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
432 ospoll->num--;
433 #endif
434 #if PORT
435 struct ospollfd *osfd = ospoll->fds[pos];
436 port_dissociate(ospoll->epoll_fd, PORT_SOURCE_FD, fd);
438 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
439 ospoll->num--;
440 osfd->callback = NULL;
441 osfd->data = NULL;
442 xorg_list_add(&osfd->deleted, &ospoll->deleted);
443 #endif
444 #if EPOLL
445 struct ospollfd *osfd = ospoll->fds[pos];
446 struct epoll_event ev;
447 ev.events = 0;
448 ev.data.ptr = osfd;
449 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_DEL, fd, &ev);
451 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
452 ospoll->num--;
453 osfd->callback = NULL;
454 osfd->data = NULL;
455 xorg_list_add(&osfd->deleted, &ospoll->deleted);
456 #endif
457 #if POLL
458 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
459 array_delete(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
460 ospoll->num--;
461 ospoll->changed = TRUE;
462 #endif
466 #if PORT
467 static void
468 epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
470 int events = 0;
471 if (osfd->xevents & X_NOTIFY_READ)
472 events |= POLLIN;
473 if (osfd->xevents & X_NOTIFY_WRITE)
474 events |= POLLOUT;
475 port_associate(ospoll->epoll_fd, PORT_SOURCE_FD, osfd->fd, events, osfd);
477 #endif
479 #if EPOLL
480 static void
481 epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
483 struct epoll_event ev;
484 ev.events = 0;
485 if (osfd->xevents & X_NOTIFY_READ)
486 ev.events |= EPOLLIN;
487 if (osfd->xevents & X_NOTIFY_WRITE)
488 ev.events |= EPOLLOUT;
489 if (osfd->trigger == ospoll_trigger_edge)
490 ev.events |= EPOLLET;
491 ev.data.ptr = osfd;
492 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_MOD, osfd->fd, &ev);
494 #endif
496 void
497 ospoll_listen(struct ospoll *ospoll, int fd, int xevents)
499 int pos = ospoll_find(ospoll, fd);
501 if (pos >= 0) {
502 #if POLLSET
503 struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd };
504 if (xevents & X_NOTIFY_READ) {
505 ctl.events |= POLLIN;
506 ospoll->fds[pos].revents &= ~POLLIN;
508 if (xevents & X_NOTIFY_WRITE) {
509 ctl.events |= POLLOUT;
510 ospoll->fds[pos].revents &= ~POLLOUT;
512 pollset_ctl(ospoll->ps, &ctl, 1);
513 ospoll->fds[pos].xevents |= xevents;
514 #endif
515 #if EPOLL || PORT
516 struct ospollfd *osfd = ospoll->fds[pos];
517 osfd->xevents |= xevents;
518 epoll_mod(ospoll, osfd);
519 #endif
520 #if POLL
521 if (xevents & X_NOTIFY_READ) {
522 ospoll->fds[pos].events |= POLLIN;
523 ospoll->osfds[pos].revents &= ~POLLIN;
525 if (xevents & X_NOTIFY_WRITE) {
526 ospoll->fds[pos].events |= POLLOUT;
527 ospoll->osfds[pos].revents &= ~POLLOUT;
529 #endif
533 void
534 ospoll_mute(struct ospoll *ospoll, int fd, int xevents)
536 int pos = ospoll_find(ospoll, fd);
538 if (pos >= 0) {
539 #if POLLSET
540 struct ospollfd *osfd = &ospoll->fds[pos];
541 osfd->xevents &= ~xevents;
542 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
543 pollset_ctl(ospoll->ps, &ctl, 1);
544 if (osfd->xevents) {
545 ctl.cmd = PS_ADD;
546 if (osfd->xevents & X_NOTIFY_READ) {
547 ctl.events |= POLLIN;
549 if (osfd->xevents & X_NOTIFY_WRITE) {
550 ctl.events |= POLLOUT;
552 pollset_ctl(ospoll->ps, &ctl, 1);
554 #endif
555 #if EPOLL || PORT
556 struct ospollfd *osfd = ospoll->fds[pos];
557 osfd->xevents &= ~xevents;
558 epoll_mod(ospoll, osfd);
559 #endif
560 #if POLL
561 if (xevents & X_NOTIFY_READ)
562 ospoll->fds[pos].events &= ~POLLIN;
563 if (xevents & X_NOTIFY_WRITE)
564 ospoll->fds[pos].events &= ~POLLOUT;
565 #endif
571 ospoll_wait(struct ospoll *ospoll, int timeout)
573 int nready;
574 #if POLLSET
575 #define MAX_EVENTS 256
576 struct pollfd events[MAX_EVENTS];
578 nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout);
579 for (int i = 0; i < nready; i++) {
580 struct pollfd *ev = &events[i];
581 int pos = ospoll_find(ospoll, ev->fd);
582 struct ospollfd *osfd = &ospoll->fds[pos];
583 short revents = ev->revents;
584 short oldevents = osfd->revents;
586 osfd->revents = (revents & (POLLIN|POLLOUT));
587 if (osfd->trigger == ospoll_trigger_edge)
588 revents &= ~oldevents;
589 if (revents) {
590 int xevents = 0;
591 if (revents & POLLIN)
592 xevents |= X_NOTIFY_READ;
593 if (revents & POLLOUT)
594 xevents |= X_NOTIFY_WRITE;
595 if (revents & (~(POLLIN|POLLOUT)))
596 xevents |= X_NOTIFY_ERROR;
597 osfd->callback(osfd->fd, xevents, osfd->data);
600 #endif
601 #if PORT
602 #define MAX_EVENTS 256
603 port_event_t events[MAX_EVENTS];
604 uint_t nget = 1;
605 timespec_t port_timeout = {
606 .tv_sec = timeout / 1000,
607 .tv_nsec = (timeout % 1000) * 1000000
610 nready = 0;
611 if (port_getn(ospoll->epoll_fd, events, MAX_EVENTS, &nget, &port_timeout)
612 == 0) {
613 nready = nget;
615 for (int i = 0; i < nready; i++) {
616 port_event_t *ev = &events[i];
617 struct ospollfd *osfd = ev->portev_user;
618 uint32_t revents = ev->portev_events;
619 int xevents = 0;
621 if (revents & POLLIN)
622 xevents |= X_NOTIFY_READ;
623 if (revents & POLLOUT)
624 xevents |= X_NOTIFY_WRITE;
625 if (revents & (~(POLLIN|POLLOUT)))
626 xevents |= X_NOTIFY_ERROR;
628 if (osfd->callback)
629 osfd->callback(osfd->fd, xevents, osfd->data);
631 if (osfd->trigger == ospoll_trigger_level &&
632 !xorg_list_is_empty(&osfd->deleted)) {
633 epoll_mod(ospoll, osfd);
636 ospoll_clean_deleted(ospoll);
637 #endif
638 #if EPOLL
639 #define MAX_EVENTS 256
640 struct epoll_event events[MAX_EVENTS];
641 int i;
643 nready = epoll_wait(ospoll->epoll_fd, events, MAX_EVENTS, timeout);
644 for (i = 0; i < nready; i++) {
645 struct epoll_event *ev = &events[i];
646 struct ospollfd *osfd = ev->data.ptr;
647 uint32_t revents = ev->events;
648 int xevents = 0;
650 if (revents & EPOLLIN)
651 xevents |= X_NOTIFY_READ;
652 if (revents & EPOLLOUT)
653 xevents |= X_NOTIFY_WRITE;
654 if (revents & (~(EPOLLIN|EPOLLOUT)))
655 xevents |= X_NOTIFY_ERROR;
657 if (osfd->callback)
658 osfd->callback(osfd->fd, xevents, osfd->data);
660 ospoll_clean_deleted(ospoll);
661 #endif
662 #if POLL
663 nready = xserver_poll(ospoll->fds, ospoll->num, timeout);
664 ospoll->changed = FALSE;
665 if (nready > 0) {
666 int f;
667 for (f = 0; f < ospoll->num; f++) {
668 short revents = ospoll->fds[f].revents;
669 short oldevents = ospoll->osfds[f].revents;
671 ospoll->osfds[f].revents = (revents & (POLLIN|POLLOUT));
672 if (ospoll->osfds[f].trigger == ospoll_trigger_edge)
673 revents &= ~oldevents;
674 if (revents) {
675 int xevents = 0;
676 if (revents & POLLIN)
677 xevents |= X_NOTIFY_READ;
678 if (revents & POLLOUT)
679 xevents |= X_NOTIFY_WRITE;
680 if (revents & (~(POLLIN|POLLOUT)))
681 xevents |= X_NOTIFY_ERROR;
682 ospoll->osfds[f].callback(ospoll->fds[f].fd, xevents,
683 ospoll->osfds[f].data);
685 /* Check to see if the arrays have changed, and just go back
686 * around again
688 if (ospoll->changed)
689 break;
693 #endif
694 return nready;
697 void
698 ospoll_reset_events(struct ospoll *ospoll, int fd)
700 #if POLLSET
701 int pos = ospoll_find(ospoll, fd);
703 if (pos < 0)
704 return;
706 ospoll->fds[pos].revents = 0;
707 #endif
708 #if PORT
709 int pos = ospoll_find(ospoll, fd);
711 if (pos < 0)
712 return;
714 epoll_mod(ospoll, ospoll->fds[pos]);
715 #endif
716 #if POLL
717 int pos = ospoll_find(ospoll, fd);
719 if (pos < 0)
720 return;
722 ospoll->osfds[pos].revents = 0;
723 #endif
726 void *
727 ospoll_data(struct ospoll *ospoll, int fd)
729 int pos = ospoll_find(ospoll, fd);
731 if (pos < 0)
732 return NULL;
733 #if POLLSET
734 return ospoll->fds[pos].data;
735 #endif
736 #if EPOLL || PORT
737 return ospoll->fds[pos]->data;
738 #endif
739 #if POLL
740 return ospoll->osfds[pos].data;
741 #endif