1 /******************************************************************************
2 
3     Fiber that can register select clients in a dispatcher, optimizing
4     re-registrations and event changes.
5 
6     MessageFiber that includes a select dispatcher and memorizes the last client
7     it has registered to optimize registrations by skipping unnecessary
8     register() or unregister() calls.
9 
10     Copyright:
11         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
12         All rights reserved.
13 
14     License:
15         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
16         Alternatively, this file may be distributed under the terms of the Tango
17         3-Clause BSD License (see LICENSE_BSD.txt for details).
18 
19  ******************************************************************************/
20 
21 module ocean.io.select.fiber.SelectFiber;
22 
23 
24 import ocean.core.MessageFiber;
25 
26 import ocean.core.Verify;
27 
28 import core.thread;
29 
30 import ocean.io.select.client.model.ISelectClient;
31 import ocean.io.select.client.model.ISelectClientInfo;
32 
33 import ocean.io.select.EpollSelectDispatcher;
34 
35 debug ( SelectFiber) import ocean.io.Stdout : Stderr;
36 
37 
38 /******************************************************************************/
39 
40 public class SelectFiber : MessageFiber
41 {
42     /**************************************************************************
43 
44         Epoll instance to use
45 
46      **************************************************************************/
47 
48     public EpollSelectDispatcher epoll;
49 
50     /**************************************************************************
51 
52         Currently registered select client
53 
54      **************************************************************************/
55 
56     private ISelectClient current = null;
57 
58     /**************************************************************************
59 
60         Constructor
61 
62         Params:
63            epoll = EpollSelectDispatcher instance
64            fiber = already created core.thread.Fiber
65 
66      **************************************************************************/
67 
68     this ( EpollSelectDispatcher epoll, Fiber fiber )
69     {
70         this.epoll = epoll;
71 
72         super(fiber);
73     }
74 
75     /**************************************************************************
76 
77         Constructor
78 
79         Params:
80            epoll = EpollSelectDispatcher instance
81            coroutine = fiber coroutine
82 
83      **************************************************************************/
84 
85     this ( EpollSelectDispatcher epoll, scope void delegate ( ) coroutine )
86     {
87         this.epoll = epoll;
88 
89         super(coroutine);
90     }
91 
92     /**************************************************************************
93 
94         Constructor
95 
96         Params:
97             epoll = EpollSelectDispatcher instance
98             coroutine = fiber coroutine
99             sz      = fiber stack size
100 
101      **************************************************************************/
102 
103     this ( EpollSelectDispatcher epoll, scope void delegate ( ) coroutine, size_t sz )
104     {
105         this.epoll = epoll;
106 
107         super(coroutine, sz);
108     }
109 
110     /**************************************************************************
111 
112         Allows to change underlying core.thread.Fiber instance. Unregisters
113         the ISelectClient if necessary.
114 
115         Params:
116             fiber = new fiber instance to use
117 
118     **************************************************************************/
119 
120     override public void reset ( Fiber fiber )
121     {
122         this.unregister();
123         super.reset(fiber);
124     }
125 
126     /**************************************************************************
127 
128         Registers client in epoll and sets client to the current client.
129 
130         Params:
131             client = select client to register
132 
133         Returns:
134             true if an epoll registration was actually added or modified or
135             false if the client was the currently registered client.
136 
137      **************************************************************************/
138 
139     public bool register ( ISelectClient client )
140     {
141         verify(client !is null);
142 
143         debug ( SelectFiber) Stderr.formatln("{}.register fd {}:",
144                 typeof(this).stringof, client.fileHandle);
145 
146         if ( this.current is null )
147         {
148             // No client is currently registered: Add an epoll registration for
149             // the a new client.
150 
151             debug ( SelectFiber) Stderr.formatln("   Register new {}", client);
152 
153             this.epoll.register(this.current = client);
154 
155             return true;
156         }
157         else if ( this.current is client )
158         {
159             // The currently registered client is used for another I/O
160             // operation: Only refresh the timeout registration of the client,
161             // no need to change the epoll registration.
162 
163             debug ( SelectFiber)
164             {
165                 Stderr.formatln("   Leaving registered {}", this.current);
166             }
167 
168             // As there is not way to modify a registration with the
169             // timeout manager, it is necessary to call unregistered(), then
170             // registered() even if this.current and client are identical. This
171             // ensures that, even if the epoll registration doesn't need to be
172             // updated, that the timeout timeout registration is updated
173             // correctly.
174 
175             this.current.unregistered();
176             client.registered();
177 
178             return false;
179         }
180         else if ( this.current.fileHandle == client.fileHandle )
181         {
182             // The currently registered client and the new client share the same
183             // I/O device: Update the epoll registration of the I/O device to
184             // the new client.
185 
186             debug ( SelectFiber)
187             {
188                 Stderr.formatln("   Changing event registration {}",
189                     this.current);
190                 Stderr.formatln("   Register {}", client);
191             }
192 
193             this.epoll.changeClient(this.current, client);
194 
195             this.current = client;
196 
197             return true;
198         }
199         else
200         {
201             // The currently registered client and the new client have different
202             // I/O devices: Unregister the epoll registration of the current
203             // client and register the new client instead.
204 
205             debug ( SelectFiber) Stderr.formatln("   Unregister {}",
206                 this.current);
207 
208             this.epoll.unregister(this.current);
209 
210             debug ( SelectFiber) Stderr.formatln("   Register {}", client);
211 
212             this.epoll.register(this.current = client);
213 
214             return true;
215         }
216     }
217 
218     /**************************************************************************
219 
220         Unegisters the current client from epoll and clears it, if any.
221 
222         Returns:
223             true if the current client was unregistered or false if there was
224             no current client.
225 
226      **************************************************************************/
227 
228     public bool unregister ( )
229     {
230         if ( this.current !is null )
231         {
232             debug ( SelectFiber) Stderr.formatln("{}.unregister fd {}",
233                     typeof(this).stringof, this.current.fileHandle);
234 
235             this.epoll.unregister(this.current);
236             this.current = null;
237 
238             return true;
239         }
240         else
241         {
242             return false;
243         }
244     }
245 
246     /**************************************************************************
247 
248         Checks if client is identical to the current client.
249         Note that the client instance is compared, not the client conduit,
250         file descriptor or events.
251 
252         Params:
253             client = client to compare for identity with the current client,
254                      pass null to check if there is no current client.
255 
256         Returns:
257             true if client is the current client or false otherwise.
258 
259      **************************************************************************/
260 
261     public bool isRegistered ( ISelectClient client )
262     {
263         return this.current is client;
264     }
265 
266     /**************************************************************************
267 
268         Clears the current client; usually called from the client finalizer.
269 
270         Note that the client does not need to be unregistered here, as the epoll
271         selector always unregisters the client after calling its finalizer.
272 
273         Returns:
274             true if there actually was a current client or false otherwise.
275 
276      **************************************************************************/
277 
278     public bool clear ( )
279     {
280         scope (success) this.current = null;
281 
282         return this.current !is null;
283     }
284 
285     /**************************************************************************
286 
287         Returns:
288             informational interface to currently registered client (null if no
289             client is registered)
290 
291      **************************************************************************/
292 
293     public ISelectClientInfo registered_client ( )
294     {
295         return this.current;
296     }
297 }
298 
299