1 /* AirScan (a.k.a. eSCL) backend for SANE
2  *
3  * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4  * See LICENSE for license terms and conditions
5  *
6  * Pollable events
7  */
8 
9 #include "airscan.h"
10 
11 #ifdef OS_HAVE_EVENTFD
12 #include <sys/eventfd.h>
13 #endif
14 #include <poll.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 
18 #pragma GCC diagnostic ignored "-Wunused-result"
19 
20 /* The pollable event
21  */
22 struct pollable {
23     int efd; /* Underlying eventfd handle */
24 #ifndef OS_HAVE_EVENTFD
25     // Without eventfd we use a pipe, so we need a second fd.
26     int write_fd;
27 #endif
28 };
29 
30 /* Create new pollable event
31  */
32 pollable*
pollable_new(void)33 pollable_new (void)
34 {
35 #ifdef OS_HAVE_EVENTFD
36     int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
37 #else
38     int fds[2];
39     int r = pipe2(fds, O_CLOEXEC | O_NONBLOCK);
40     int efd = r < 0 ? r : fds[0];
41 #endif
42     if (efd< 0) {
43         return NULL;
44     }
45 
46     pollable *p = mem_new(pollable, 1);
47     p->efd = efd;
48 #ifndef OS_HAVE_EVENTFD
49     p->write_fd = fds[1];
50 #endif
51 
52     return p;
53 }
54 
55 /* Free pollable event
56  */
57 void
pollable_free(pollable * p)58 pollable_free (pollable *p)
59 {
60     close(p->efd);
61 #ifndef OS_HAVE_EVENTFD
62     close(p->write_fd);
63 #endif
64     mem_free(p);
65 }
66 
67 /* Get file descriptor for poll()/select().
68  */
69 int
pollable_get_fd(pollable * p)70 pollable_get_fd (pollable *p)
71 {
72     return p->efd;
73 }
74 
75 /* Make pollable event "ready"
76  */
77 void
pollable_signal(pollable * p)78 pollable_signal (pollable *p)
79 {
80     static uint64_t c = 1;
81 #ifdef OS_HAVE_EVENTFD
82     write(p->efd, &c, sizeof(c));
83 #else
84     write(p->write_fd, &c, sizeof(c));
85 #endif
86 }
87 
88 /* Make pollable event "not ready"
89  */
90 void
pollable_reset(pollable * p)91 pollable_reset (pollable *p)
92 {
93     uint64_t unused;
94 
95     (void) read(p->efd, &unused, sizeof(unused));
96 }
97 
98 /* Wait until pollable event is ready
99  */
100 void
pollable_wait(pollable * p)101 pollable_wait (pollable *p)
102 {
103     int rc;
104 
105     do {
106         struct pollfd pfd = {
107             .fd = p->efd,
108             .events = POLLIN,
109             .revents = 0
110         };
111         rc = poll(&pfd, 1, -1);
112     } while (rc < 1);
113 }
114 
115 /* vim:ts=8:sw=4:et
116  */
117