1 /*******************************************************************************
2 
3     Linux custom file descriptor event.
4 
5     eventfd man page follows:
6 
7     Creates an "eventfd object" that can be used as an event wait/notify
8     mechanism by userspace applications, and by the kernel to notify userspace
9     applications of events. The object contains an unsigned 64-bit integer
10     (ulong) counter that is maintained  by the kernel.
11 
12     The following operations can be performed on the file descriptor:
13 
14     read(2)
15         If the eventfd counter has a nonzero value, then a read(2) returns 8
16         bytes containing that value, and the counter's value is reset to zero.
17         (The returned value is in host byte order, i.e., the native byte order
18         for integers on the host machine.)
19         If the counter is zero at the time of the read(2), then the call either
20         blocks until the counter becomes nonzero, or fails with the error EAGAIN
21         if the file descriptor has been made non-blocking (via the use of the
22         fcntl(2) F_SETFL operation to set the O_NONBLOCK flag).
23 
24         A read(2) will fail with the error EINVAL if the size of the supplied
25         buffer is less than 8 bytes.
26 
27     write(2)
28         A write(2) call adds the 8-byte integer value supplied in its buffer to
29         the counter. The maximum value that may be stored in the counter is the
30         largest unsigned 64-bit value minus 1 (i.e., 0xfffffffffffffffe). If
31         the addition would cause the counter's value to exceed the maximum,
32         then the write(2) either blocks until a read(2) is performed on the file
33         descriptor, or fails with the error EAGAIN if the file descriptor has
34         been made non-blocking.
35         A write(2) will fail with the error EINVAL if the size of the supplied
36         buffer is less than 8 bytes, or if an attempt is made to write the value
37         0xffffffffffffffff.
38 
39     poll(2), select(2) (and similar)
40         The returned file descriptor supports poll(2) (and analogously epoll(7))
41         and select(2), as follows:
42 
43         The file descriptor is readable (the select(2) readfds argument; the
44         poll(2) POLLIN flag) if the counter has a value greater than 0.
45 
46         The file descriptor is writable (the select(2) writefds argument; the
47         poll(2) POLLOUT flag) if it is possible to write a value of at least
48         "1" without blocking.
49 
50         The file descriptor indicates an exceptional condition (the select(2)
51         exceptfds argument; the poll(2) POLLERR flag) if an overflow of the
52         counter value was detected. As noted above, write(2) can never overflow
53         the counter. However an overflow can occur if 2^64 eventfd "signal
54         posts" were performed by the KAIO subsystem (theoretically possible,
55         but practically unlikely). If an overflow has occurred, then read(2)
56         will return that maximum uint64_t value (i.e., 0xffffffffffffffff). The
57         eventfd file descriptor also supports the other file-descriptor
58         multiplexing APIs: pselect(2), ppoll(2), and epoll(7).
59 
60     close(2)
61         When the file descriptor is no longer required it should be closed.
62         When all file descriptors associated with the same eventfd object have
63         been closed, the resources for object are freed by the kernel.
64 
65     A copy of the file descriptor created by eventfd() is inherited by the child
66     produced by fork(2). The duplicate file descriptor is associated with the
67     same eventfd object. File descriptors created by eventfd() are preserved
68     across execve(2).
69 
70     Copyright:
71         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
72         All rights reserved.
73 
74     License:
75         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
76         Alternatively, this file may be distributed under the terms of the Tango
77         3-Clause BSD License (see LICENSE_BSD.txt for details).
78 
79 *******************************************************************************/
80 
81 module ocean.sys.EventFD;
82 
83 import ocean.io.model.IConduit;
84 
85 import core.sys.posix.sys.types: ssize_t;
86 import core.sys.posix.unistd: read, write, close;
87 
88 /*******************************************************************************
89 
90     Definition of external functions required to manage custom events.
91 
92     Params:
93         initval = initial counter value
94         flags   = Starting with Linux 2.6.27: 0 or a bitwise OR combination of
95                   - EFD_NONBLOCK: Set the O_NONBLOCK file status flag on the
96                         new open file description.
97                   - EFD_CLOEXEC: Set the close-on-exec (FD_CLOEXEC) flag on
98                         the new file descriptor. (See the description of the
99                         O_CLOEXEC  flag  in open(2) for reasons why this may be
100                         useful.)
101 
102                   Up to Linux version 2.6.26: Must be 0.
103 
104     Returns:
105         new file descriptor that can be used to refer to the eventfd object
106 
107 *******************************************************************************/
108 
109 private extern ( C )
110 {
111     int eventfd ( uint initval, int flags );
112     static immutable EFD_CLOEXEC = 0x80000;
113 }
114 
115 
116 
117 /*******************************************************************************
118 
119     Event fd class
120 
121 *******************************************************************************/
122 
123 public class EventFD : ISelectable
124 {
125     import ocean.sys.CloseOnExec;
126 
127     /***************************************************************************
128 
129         Integer file descriptor provided by the operating system and used to
130         manage the custom event.
131 
132     ***************************************************************************/
133 
134     private int fd;
135 
136 
137     /***************************************************************************
138 
139         Constructor. Creates a custom event file descriptor.
140 
141     ***************************************************************************/
142 
143     public this ( )
144     {
145         this.fd = .eventfd(0, setCloExec(0, EFD_CLOEXEC));
146     }
147 
148 
149     /***************************************************************************
150 
151         Destructor. Destroys the event file descriptor.
152 
153     ***************************************************************************/
154 
155     ~this ( )
156     {
157         .close(this.fd);
158     }
159 
160 
161     /***************************************************************************
162 
163         Required by ISelectable interface.
164 
165         Returns:
166             file descriptor used to manage custom event
167 
168     ***************************************************************************/
169 
170     public Handle fileHandle ( )
171     {
172         return cast(Handle)this.fd;
173     }
174 
175 
176     /***************************************************************************
177 
178         Triggers the custom event one or more times.
179 
180         Params:
181             n = number of times to trigger the event (defaults to 1)
182 
183     ***************************************************************************/
184 
185     public void trigger ( ulong n = 1 )
186     {
187         this.write(n);
188     }
189 
190 
191     /***************************************************************************
192 
193         Should be called when the custom event has fired.
194 
195         Returns:
196             the number of times the event has been triggered since the last call
197             to handle().
198 
199     ***************************************************************************/
200 
201     public ulong handle ( )
202     {
203         ulong n;
204         this.read(n);
205 
206         return n;
207     }
208 
209 
210     /***************************************************************************
211 
212         Writes to the custom event file descriptor.
213 
214         A write() call adds the ulong value supplied in its buffer to the
215         counter. The maximum value that may be stored in the counter is
216         ulong.max - 1. If the addition would cause the counter's value to
217         exceed the maximum, write() either blocks until a read() is
218         performed or fails with the error EAGAIN if the file descriptor has
219         been made non-blocking.
220         A write() will fail with the error EINVAL if an attempt is made to
221         write the value ulong.max.
222 
223         Params:
224             n = value to write
225 
226         Returns:
227             ulong.sizeof on success or -1 on error. For -1 errno is set
228             appropriately.
229 
230     ***************************************************************************/
231 
232     private ssize_t write ( ulong n )
233     {
234         return .write(this.fd, &n, n.sizeof);
235     }
236 
237 
238     /***************************************************************************
239 
240         Reads from the custom event file descriptor.
241 
242         If the eventfd counter has a nonzero value, then a read() returns
243         that value, and the counter's value is reset to zero.
244         If the counter is zero at the time of the read(), then the call
245         either blocks until the counter becomes nonzero, or fails with the
246         error EAGAIN if the file descriptor has been made non-blocking.
247 
248         Params:
249             n = value output
250 
251         Returns:
252             ulong.sizeof on success, 0 on end-of-file condition or -1 on
253             error. For 0 and -1 errno is set appropriately.
254 
255     ***************************************************************************/
256 
257     private ssize_t read ( out ulong n )
258     {
259         return .read(this.fd, &n, n.sizeof);
260     }
261 }