1 /*****************************************************************************
2 
3     Linux Epoll API binding and utility struct.
4 
5     Copyright:
6         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14  *****************************************************************************/
15 
16 module ocean.sys.Epoll;
17 
18 
19 import ocean.meta.types.Qualifiers;
20 import ocean.core.Verify;
21 import core.sys.posix.unistd: close;
22 
23 version (unittest)
24 {
25     debug = EpollFdSanity;
26     import ocean.core.Test;
27 }
28 
29 /*****************************************************************************
30 
31     Struct bundling the event to register a file descriptor for with an
32     attachment.
33 
34  *****************************************************************************/
35 
36 align (1) struct epoll_event_t
37 {
38     /**************************************************************************
39 
40         Events supported by epoll, can be OR-combined.
41 
42      **************************************************************************/
43 
44     enum Event : uint
45     {
46         None            = 0,
47 
48         /**********************************************************************
49 
50             The associated file is available for read(2) operations.
51 
52          **********************************************************************/
53 
54         EPOLLIN         = 0x001,
55 
56         /**********************************************************************
57 
58             There is urgent data available for read(2) operations.
59 
60          **********************************************************************/
61 
62         EPOLLPRI        = 0x002,
63 
64         /**********************************************************************
65 
66             The associated file is available for write(2) operations.
67 
68          **********************************************************************/
69 
70         EPOLLOUT        = 0x004,
71 
72         EPOLLRDNORM     = 0x040,
73         EPOLLRDBAND     = 0x080,
74         EPOLLWRNORM     = 0x100,
75         EPOLLWRBAND     = 0x200,
76         EPOLLMSG        = 0x400,
77 
78         /**********************************************************************
79 
80             Error condition happened on the associated file descriptor.
81             epoll_wait(2) will always wait for this event; it is not necessary
82             to set it in events.
83 
84          **********************************************************************/
85 
86         EPOLLERR        = 0x008,
87 
88         /**********************************************************************
89 
90             Hang up happened on the associated file descriptor. epoll_wait(2)
91             will always wait for this event; it is not necessary to set it in
92             events.
93 
94          **********************************************************************/
95 
96         EPOLLHUP        = 0x010,
97 
98         /**********************************************************************
99 
100             (since Linux 2.6.17)
101             Stream socket peer closed connection, or shut down writing half of
102             connection. (This flag is especially useful for writing simple code
103             to detect peer shutdown when using Edge Triggered monitoring.)
104 
105          **********************************************************************/
106 
107         EPOLLRDHUP      = 0x2000,
108 
109         /**********************************************************************
110 
111             (since Linux 2.6.2)
112             Sets the one-shot behavior for the associated file descriptor. This
113             means that after an event is pulled out with epoll_wait(2) the
114             associated file descriptor is internally disabled and no other
115             events will be reported by the epoll interface. The user must call
116             epoll_ctl() with EPOLL_CTL_MOD to rearm the file descriptor with a
117             new event mask.
118 
119          **********************************************************************/
120 
121         EPOLLONESHOT    = 1u << 30,
122 
123         /**********************************************************************
124 
125             Sets the Edge Triggered behavior for the associated file descriptor.
126             The default behavior for epoll is Level Triggered. See epoll(7) for
127             more detailed information about Edge and Level Triggered event
128             distribution architectures.
129 
130          **********************************************************************/
131 
132         EPOLLET         = 1u << 31
133     }
134 
135     /**************************************************************************
136 
137         Mapping from Event -> string, useful for printouts
138 
139      **************************************************************************/
140 
141     static istring[Event] event_to_name;
142 
143     static this ( )
144     {
145         with ( Event )
146         {
147             event_to_name[EPOLLIN]       = "Re";
148             event_to_name[EPOLLPRI]      = "Pr";
149             event_to_name[EPOLLOUT]      = "Wr";
150             event_to_name[EPOLLRDNORM]   = "Rn";
151             event_to_name[EPOLLRDBAND]   = "Rb";
152             event_to_name[EPOLLWRNORM]   = "Wn";
153             event_to_name[EPOLLWRBAND]   = "Wb";
154             event_to_name[EPOLLMSG]      = "Ms";
155             event_to_name[EPOLLERR]      = "Er";
156             event_to_name[EPOLLHUP]      = "Hu";
157             event_to_name[EPOLLRDHUP]    = "Rh";
158             event_to_name[EPOLLONESHOT]  = "Os";
159             event_to_name[EPOLLET]       = "Et";
160         }
161         event_to_name.rehash;
162     }
163 
164     /**************************************************************************
165 
166         Convenience type alias
167 
168      **************************************************************************/
169 
170     alias .epoll_data_t Data;
171 
172     /**************************************************************************
173 
174         Epoll events
175 
176      **************************************************************************/
177 
178     Event events;
179 
180     /**************************************************************************
181 
182         User data variable
183 
184      **************************************************************************/
185 
186     Data  data;
187 }
188 
189 /******************************************************************************
190 
191     Epoll user data union
192 
193  ******************************************************************************/
194 
195 align (1) union epoll_data_t
196 {
197     void*   ptr;
198     int     fd;
199     uint    u32;
200     ulong   u64;
201 
202     /**************************************************************************
203 
204         Sets ptr to o.
205 
206         Params:
207             o = object to set ptr to
208 
209         Returns:
210             ptr cast back to Object
211 
212      **************************************************************************/
213 
214     Object obj ( Object o )
215     {
216         return cast (Object) (this.ptr = cast (void*) o);
217     }
218 
219     /**************************************************************************
220 
221         Obtains the object to which ptr should previously have been set.
222 
223         Returns:
224             ptr cast back to Object
225 
226      **************************************************************************/
227 
228     Object obj ( )
229     {
230         return cast (Object) this.ptr;
231     }
232 }
233 
234 /******************************************************************************
235 
236     Flags accepted by epoll_create1(), can be OR-combined.
237 
238  ******************************************************************************/
239 
240 enum EpollCreateFlags
241 {
242     None            = 0,
243 
244     /**************************************************************************
245 
246         Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.
247         See the description of the O_CLOEXEC flag in open(2) for reasons why
248         this may be useful.
249 
250      **************************************************************************/
251 
252     EPOLL_CLOEXEC   = 0x8_0000, // 02000000
253 
254     EPOLL_NONBLOCK  = 0x800     // 04000
255 }
256 
257 /******************************************************************************
258 
259     epoll_ctl opcodes.
260 
261 ******************************************************************************/
262 
263 enum EpollCtlOp : int
264 {
265     /**************************************************************************
266 
267         Register the target file descriptor fd on the epoll instance referred to
268         by the file descriptor epfd and associate the event event with the
269         internal file linked to fd.
270 
271      **************************************************************************/
272 
273     EPOLL_CTL_ADD = 1,
274 
275     /**************************************************************************
276 
277         Remove (deregister) the target file descriptor fd from the epoll
278         instance referred to by epfd. The event is ignored; it and can be null
279         on Linux 2.6.9 or later.
280 
281      **************************************************************************/
282 
283     EPOLL_CTL_DEL = 2,
284 
285     /**************************************************************************
286 
287         Change the event event associated with the target file descriptor fd.
288 
289      **************************************************************************/
290 
291     EPOLL_CTL_MOD = 3
292 }
293 
294 
295 extern (C)
296 {
297     /**************************************************************************
298 
299         Description
300 
301         epoll_create1() creates an epoll "instance", requesting the kernel to
302         allocate an event backing store dimensioned for size descriptors.
303         epoll_create1() returns a file descriptor referring to the new epoll
304         instance. This file descriptor is used for all the subsequent calls to
305         the epoll interface. When no longer required, the file descriptor
306         returned by epoll_create1() should be closed by using close(2). When all
307         file descriptors referring to an epoll instance have been closed, the
308         kernel destroys the instance and releases the associated resources for
309         reuse.
310 
311         The following value can be included in flags:
312 
313         EPOLL_CLOEXEC
314             Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.
315             See the description of the O_CLOEXEC flag in open(2) for reasons why
316             this may be useful.
317 
318         Return Value
319 
320         On success, these system calls return a nonnegative file descriptor.
321         On error, -1 is returned, and errno is set to indicate the error.
322 
323         Errors
324 
325         EINVAL
326             size is not positive.
327         EINVAL
328             Invalid value specified in flags.
329         EMFILE
330             The per-user limit on the number of epoll instances imposed by
331             /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7)
332             for further details.
333         ENFILE
334             The system limit on the total number of open files has been reached.
335         ENOMEM
336             There was insufficient memory to create the kernel object.
337 
338         Versions
339             epoll_create1() was added to the kernel in version 2.6.27. Library
340             support is provided in glibc starting with version 2.9.
341 
342      **************************************************************************/
343 
344     int epoll_create1(EpollCreateFlags flags = EpollCreateFlags.None);
345 
346     /**************************************************************************
347 
348         Description
349 
350         This system call performs control operations on the epoll instance
351         referred to by the file descriptor epfd. It requests that the operation
352         op be performed for the target file descriptor, fd.
353 
354         Valid values for the op argument are:
355 
356         EPOLL_CTL_ADD
357             Register the target file descriptor fd on the epoll instance
358             referred to by the file descriptor epfd and associate the event
359             event with the internal file linked to fd.
360         EPOLL_CTL_MOD
361             Change the event event associated with the target file descriptor
362             fd.
363         EPOLL_CTL_DEL
364             Remove (deregister) the target file descriptor fd from the epoll
365             instance referred to by epfd. The event is ignored; it and can be null
366             on Linux 2.6.9 or later.
367 
368         The event argument describes the object linked to the file descriptor
369         fd. The events member is a bit set composed using the following
370         available event types:
371 
372         EPOLLIN
373             The associated file is available for read(2) operations.
374         EPOLLOUT
375             The associated file is available for write(2) operations.
376         EPOLLRDHUP (since Linux 2.6.17)
377             Stream socket peer closed connection, or shut down writing half of
378             connection. (This flag is especially useful for writing simple code
379             to detect peer shutdown when using Edge Triggered monitoring.)
380         EPOLLPRI
381             There is urgent data available for read(2) operations.
382         EPOLLERR
383             Error condition happened on the associated file descriptor.
384             epoll_wait(2) will always wait for this event; it is not necessary
385             to set it in events.
386         EPOLLHUP
387             Hang up happened on the associated file descriptor. epoll_wait(2)
388             will always wait for this event; it is not necessary to set it in
389             events.
390         EPOLLET
391             Sets the Edge Triggered behavior for the associated file descriptor.
392             The default behavior for epoll is Level Triggered. See epoll(7) for
393             more detailed information about Edge and Level Triggered event
394             distribution architectures.
395         EPOLLONESHOT (since Linux 2.6.2)
396             Sets the one-shot behavior for the associated file descriptor. This
397             means that after an event is pulled out with epoll_wait(2) the
398             associated file descriptor is internally disabled and no other
399             events will be reported by the epoll interface. The user must call
400             epoll_ctl() with EPOLL_CTL_MOD to rearm the file descriptor with a
401             new event mask.
402 
403         Return Value
404 
405         When successful, epoll_ctl() returns zero. When an error occurs,
406         epoll_ctl() returns -1 and errno is set appropriately.
407 
408         Errors
409 
410         EBADF
411             epfd or fd is not a valid file descriptor.
412         EEXIST
413             op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already
414             registered with this epoll instance.
415         EINVAL
416             epfd is not an epoll file descriptor, or fd is the same as epfd, or
417             the requested operation op is not supported by this interface.
418         ENOENT
419             op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with
420             this epoll instance.
421         ENOMEM
422             There was insufficient memory to handle the requested op control
423             operation.
424         ENOSPC
425             The limit imposed by /proc/sys/fs/epoll/max_user_watches was
426             encountered while trying to register (EPOLL_CTL_ADD) a new file
427             descriptor on an epoll instance. See epoll(7) for further details.
428         EPERM
429             The target file fd does not support epoll.
430 
431         Versions
432 
433         epoll_ctl() was added to the kernel in version 2.6.
434 
435      **************************************************************************/
436 
437     int epoll_ctl(int epfd, EpollCtlOp op, int fd, epoll_event_t* event);
438 
439     /**************************************************************************
440 
441         Description
442 
443         The epoll_wait() system call waits for events on the epoll instance
444         referred to by the file descriptor epfd. The memory area pointed to by
445         events will contain the events that will be available for the caller.
446         Up to maxevents are returned by epoll_wait(). The maxevents argument
447         must be greater than zero.
448 
449         The call waits for a maximum time of timeout milliseconds. Specifying a
450         timeout of -1 makes epoll_wait() wait indefinitely, while specifying a
451         timeout equal to zero makes epoll_wait() to return immediately even if
452         no events are available (return code equal to zero).
453 
454         The data of each returned structure will contain the same data the user
455         set with an epoll_ctl(2) (EPOLL_CTL_ADD,EPOLL_CTL_MOD) while the events
456         member will contain the returned event bit field.
457 
458         Return Value
459 
460         When successful, epoll_wait() returns the number of file descriptors
461         ready for the requested I/O, or zero if no file descriptor became ready
462         during the requested timeout milliseconds. When an error occurs,
463         epoll_wait() returns -1 and errno is set appropriately.
464 
465         Errors
466 
467         EBADF
468             epfd is not a valid file descriptor.
469         EFAULT
470             The memory area pointed to by events is not accessible with write
471             permissions.
472         EINTR
473             The call was interrupted by a signal handler before any of the
474             requested events occurred or the timeout expired; see signal(7).
475         EINVAL
476             epfd is not an epoll file descriptor, or maxevents is less than or
477             equal to zero.
478 
479         Versions
480 
481         epoll_wait() was added to the kernel in version 2.6. Library support is
482         provided in glibc starting with version 2.3.2.
483 
484      **************************************************************************/
485 
486     int epoll_wait(int epfd, epoll_event_t* events, int maxevents, int timeout);
487 }
488 
489 /******************************************************************************
490 
491     Epoll utility struct, memorises the file descriptor obtained by create().
492 
493  ******************************************************************************/
494 
495 struct Epoll
496 {
497     import ocean.sys.CloseOnExec;
498 
499     /**************************************************************************
500 
501         Convenience aliases
502 
503      **************************************************************************/
504 
505     public alias .EpollCreateFlags CreateFlags;
506     public alias .EpollCtlOp CtlOp;
507     public alias .epoll_event_t epoll_event_t;
508     public alias .epoll_event_t.Event Event;
509 
510     /**************************************************************************
511 
512         Initial file descriptor value.
513 
514      **************************************************************************/
515 
516     public enum int fd_init = -1;
517 
518     /**************************************************************************
519 
520         epoll file descriptor.
521 
522      **************************************************************************/
523 
524     public int fd = fd_init;
525 
526     /**************************************************************************
527 
528         Calls epoll_create1() and memorises the returned file descriptor, which
529         is -1 in case of an error.
530 
531         Params:
532             flags = epoll_create1() flags
533 
534         Returns:
535             the obtained file descriptor on success or, -1 on error.
536             On error errno is set appropriately and the returned -1 is memorised
537             as file descriptor.
538 
539      **************************************************************************/
540 
541     public int create ( CreateFlags flags = CreateFlags.None )
542     {
543         return this.fd = epoll_create1(
544             setCloExec(flags, EpollCreateFlags.EPOLL_CLOEXEC)
545         );
546     }
547 
548     /**************************************************************************
549 
550         Calls epoll_ctl() using the current epoll file descriptor.
551 
552         The current epoll file descriptor should have been sucessfully obtained
553         by create() or epoll_create1() and not already been closed, otherwise
554         epoll_ctl() will fail so that this method returns -1.
555 
556         Params:
557             op    = epoll_ctl opcode
558             fd    = file descriptor to register for events
559             event = epoll_event_t struct instance containing the events to
560                     register fd for and optional user data
561 
562         Returns:
563             0 on success or -1 on error. On error errno is set appropriately.
564 
565      **************************************************************************/
566 
567     public int ctl ( CtlOp op, int fd, epoll_event_t event )
568     {
569         return epoll_ctl(this.fd, op, fd, &event);
570     }
571 
572     template ctlT ( size_t i = 0 )
573     {
574         static if (i < epoll_event_t.data.tupleof.length)
575         {
576 
577 
578             /**************************************************************************
579 
580                 Calls epoll_ctl() using the current epoll file descriptor to modify the
581                 registration of fd for events with data as user data.
582 
583                 Creates the epoll_event_t instance passed to epoll_ctl() from events and
584                 data where the type of data must match one of the epoll_data_t members.
585 
586                 The current epoll file descriptor should have been sucessfully obtained
587                 by create() or epoll_create1() and not already been closed, otherwise
588                 epoll_ctl() will fail so that this method returns -1.
589 
590                 Params:
591                     op     = epoll_ctl opcode
592                     fd     = file descriptor to register for events
593                     events = events to register fd for
594                     data   = user data; the member of the data field of the created
595                              epoll_data_t instance that matches the type is set to it
596 
597                 Returns:
598                     0 on success or -1 on error. On error errno is set appropriately.
599 
600             **************************************************************************/
601 
602             int ctl ( CtlOp op, int fd, Event events, typeof (epoll_event_t.data.tupleof[i]) data )
603             {
604                 epoll_event_t event;
605 
606                 event.events          = events;
607                 event.data.tupleof[i] = data;
608 
609                 return epoll_ctl(this.fd, op, fd, &event);
610             }
611 
612             mixin ctlT!(i + 1);
613         }
614     }
615 
616     mixin ctlT!();
617 
618     /**************************************************************************
619 
620         Calls epoll_ctl() using the current epoll file descriptor to modify the
621         registration of fd for events with fd as user data.
622 
623         Creates the epoll_event_t instance passed to epoll_ctl() from events and
624         fd where data.fd is set to fd.
625 
626         The current epoll file descriptor should have been sucessfully obtained
627         by create() or epoll_create1() and not already been closed, otherwise
628         epoll_ctl() will fail so that this method returns -1.
629 
630         Params:
631             op     = epoll_ctl opcode
632             fd     = file descriptor to register for events and to set data.fd
633                      of the created epoll_data_t instance to
634             events = events to register fd for
635 
636         Returns:
637             0 on success or -1 on error. On error errno is set appropriately.
638 
639      **************************************************************************/
640 
641     public int ctl ( CtlOp op, int fd, Event events )
642     {
643         // FIXME: Apparently the mixin ctlT!() doesn't overload properly :(
644 
645 //        return this.ctl(op, fd, events, fd);
646         return ctlT!(1).ctl(op, fd, events, fd);
647     }
648 
649     /**************************************************************************
650 
651         Calls epoll_ctl() using the current epoll file descriptor to modify the
652         registration of fd for events with obj as user data.
653 
654         Creates the epoll_event_t instance passed to epoll_ctl() from events and
655         obj where data.obj is set to obj.
656 
657         The current epoll file descriptor should have been sucessfully obtained
658         by create() or epoll_create1() and not already been closed, otherwise
659         epoll_ctl() will fail so that this method returns -1.
660 
661         Params:
662             op     = epoll_ctl opcode
663             fd     = file descriptor to register for events
664             events = events to register fd for
665             obj    = user object to set data.obj of the created epoll_data_t
666                      instance to
667 
668         Returns:
669             0 on success or -1 on error. On error errno is set appropriately.
670 
671      **************************************************************************/
672 
673     public int ctl ( CtlOp op, int fd, Event events, Object obj )
674     {
675         epoll_event_t event;
676 
677         event.events   = events;
678         event.data.obj = obj;
679 
680         return epoll_ctl(this.fd, op, fd, &event);
681     }
682 
683     /**************************************************************************
684 
685         Calls epoll_ctl() using the current epoll file descriptor to modify the
686         registration of fd for events with obj as user data.
687 
688         Creates the epoll_event_t instance passed to epoll_ctl() from events and
689         obj where data.obj is set to obj.
690 
691         The current epoll file descriptor should have been sucessfully obtained
692         by create() or epoll_create1() and not already been closed, otherwise
693         epoll_ctl() will fail so that this method returns -1.
694 
695         Params:
696             op     = epoll_ctl opcode
697             fd     = file descriptor to register for events
698             events = events to register fd for
699             u64    = user value to set data.u64 of the created epoll_data_t
700                      instance to
701 
702         Returns:
703             0 on success or -1 on error. On error errno is set appropriately.
704 
705      **************************************************************************/
706 
707     public int ctl ( CtlOp op, int fd, Event events, ulong u64 )
708     {
709         epoll_event_t event;
710 
711         event.events   = events;
712         event.data.u64 = u64;
713 
714         return epoll_ctl(this.fd, op, fd, &event);
715     }
716 
717     /**************************************************************************
718 
719         Calls epoll_wait() using the current epoll file descriptor.
720 
721         events.length specifies the maximum number of file descriptors for which
722         events can be reported with this epoll_wait() call.
723 
724         The current epoll file descriptor should have been sucessfully obtained
725         by create() or epoll_create1() and not already been closed, otherwise
726         epoll_ctl() will fail so that this method returns -1.
727 
728         Params:
729             events     = destination array for the reported events
730             timeout_ms = timeout in ms or -1 to disable timing out
731 
732         Returns:
733             on success the number of file descriptors for which at least one
734             event was reported. 0 indicates that this call timed out before an
735             event on any file descriptor occurred.
736             On error -1 is returned and errno set appropriately.
737 
738      **************************************************************************/
739 
740     public int wait ( epoll_event_t[] events, int timeout_ms = -1 )
741     out (n)
742     {
743         assert (n <= cast (int) events.length);
744     }
745     do
746     {
747         verify(events.length <= int.max);
748         return epoll_wait(this.fd, events.ptr, cast (int) events.length, timeout_ms);
749     }
750 
751     /**************************************************************************
752 
753         Calls close() to close the current epoll file descriptor.
754 
755         Returns:
756             0 on success or -1 on error. On error errno is set appropriately.
757 
758      **************************************************************************/
759 
760     public int close ( )
761     {
762         return .close(this.fd);
763     }
764 }