1 /*******************************************************************************
2 
3     As linux on 64 machines is using only up to 56 bits for the user address
4     space (see https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt) we
5     can use bits from at least 56 to 63 for stuffing in the least significant 8
6     bits of the fd into the epoll registration. This can be used to check if
7     the ISelectClient's registration triggering in epoll wait is made for the
8     previous usage of the ISelectClient (for the wrong fd). This is not 100%
9     accurate, as the file descriptors could be reused but it can be in general
10     this is the best we can to find out expired registrations that are not
11     unregistered from epoll.
12 
13     This module provides FdObjEpollData structure that binds fd
14     and the object used at the registration time, so that they both can
15     be registered with epoll and returned with epoll_wait to the user.
16 
17     Copyright:
18         Copyright (c) 2017 dunnhumby Germany GmbH.
19         All rights reserved.
20 
21     License:
22         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
23         Alternatively, this file may be distributed under the terms of the Tango
24         3-Clause BSD License (see LICENSE_BSD.txt for details).
25 
26 *******************************************************************************/
27 
28 module ocean.io.select.selector.EpollFdSanity;
29 
30 version (unittest)
31 {
32     import ocean.core.Test;
33 }
34 
35 /*******************************************************************************
36 
37     Struct binding fd and a Object.
38 
39 *******************************************************************************/
40 
41 public struct FdObjEpollData
42 {
43     /// Number of bits to store address in
44     private enum address_bits = 56;
45 
46     /// Mask for the user-space address
47     private enum address_mask = (1UL << address_bits) - 1;
48 
49     /// Mask for the storing part of the fd
50     private enum fd_mask = ~address_mask;
51 
52     static assert(address_mask + fd_mask == ulong.max);
53 
54     /// Object instance
55     public Object obj;
56 
57     /// client's fd at the time of the registration (least significant byte)
58     private ubyte fd;
59 
60     /***************************************************************************
61 
62         Encodes the Object's address and the fd's least significant byte
63         into a ulong, suitable to register with epoll.
64 
65         Params:
66             obj = Object to store in the epoll_data instance
67             fd = file descriptor to store the least significant byte of in
68                 the epoll_data instance.
69 
70         Returns:
71             combination of the Objects's current address and part of the fd
72             to register with the epoll.
73 
74     ***************************************************************************/
75 
76     public static ulong encode (Object obj, int fd)
77     {
78         return cast(ulong)(
79                (cast(ulong)cast(void*)obj & address_mask) |
80                (cast(ulong)(fd & 0xFF) << address_bits));
81     }
82 
83     /***************************************************************************
84 
85         Parses the registration to extract the Object and the fd part
86         from it. Reverses the process of encode
87 
88         Params:
89             registration = registration containing Object and accompanying
90             fd.
91 
92         Returns:
93             FdObjEpollData struct containing Object and accompanying fd.
94 
95     ***************************************************************************/
96 
97     public static FdObjEpollData decode (ulong registration)
98     {
99         FdObjEpollData data;
100         data.obj = cast(Object)cast(void*)(registration & address_mask);
101         data.fd = cast(ubyte)((registration & fd_mask) >> address_bits);
102         return data;
103     }
104 
105     /***************************************************************************
106 
107         Compares the appropriate byte of the given fd with the instance
108         of this struct.
109 
110         Params:
111             fd = fd which we want to confirmed if it was registered with
112                  the epoll.
113 
114         Returns:
115             true if fd's lowest byte and the byte stored in the registration
116             match, false otherwise
117 
118     ***************************************************************************/
119 
120     public bool verifyFd (int fd)
121     {
122         return this.fd == (fd & 0xFF);
123     }
124 }
125 
126 ///
127 unittest
128 {
129     auto client = new Object;
130     int fd = 100;
131     auto r = FdObjEpollData.decode(FdObjEpollData.encode(client, fd));
132 
133     test!("is")(client, r.obj);
134     test!("==")(r.verifyFd(fd), true);
135 }