1 /*******************************************************************************
2 
3     Handles a set of selected epoll keys and handles registered select clients
4     that timed out.
5 
6     Copyright:
7         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8         All rights reserved.
9 
10     License:
11         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12         Alternatively, this file may be distributed under the terms of the Tango
13         3-Clause BSD License (see LICENSE_BSD.txt for details).
14 
15 *******************************************************************************/
16 
17 module ocean.io.select.selector.TimeoutSelectedKeysHandler;
18 
19 
20 import ocean.core.Verify;
21 
22 import ocean.io.select.selector.SelectedKeysHandler,
23                ocean.io.select.selector.EpollException;
24 
25 import ocean.sys.Epoll: epoll_event_t;
26 
27 import ocean.io.select.client.model.ISelectClient;
28 
29 import ocean.time.timeout.model.ITimeoutManager,
30                ocean.time.timeout.model.ITimeoutClient;
31 import ocean.meta.types.Qualifiers;
32 
33 import ocean.util.container.AppendBuffer: AppendBuffer;
34 
35 import core.stdc.stdlib: bsearch, qsort;
36 
37 debug (ISelectClient) import ocean.io.Stdout;
38 
39 /******************************************************************************/
40 
41 class TimeoutSelectedKeysHandler: SelectedKeysHandler
42 {
43     /***************************************************************************
44 
45         Timeout manager instance to obtain the timed out clients.
46 
47      **************************************************************************/
48 
49     private ITimeoutManager timeout_manager;
50 
51     /***************************************************************************
52 
53         Re-usable set of timed out clients. opCall() populates this list with
54         the timed out clients as reported by timeout_manager, unregisters and
55         finalizes all timed out clients, then handles the selected keys that are
56         not in the list.
57 
58      **************************************************************************/
59 
60     private alias AppendBuffer!(ISelectClient) TimedOutClientList;
61 
62     private TimedOutClientList timed_out_clients;
63 
64     /***************************************************************************
65 
66         Constructor.
67 
68         Params:
69             unregister      = callback delegate to remove a client registration,
70                               must be available during the lifetime of this
71                               instance
72             e               = exception to keep and throw if an error event was
73                               reported for a selected key
74             timeout_manager = timeout manager to obtain the timed out clients in
75                               handle()
76             num_clients     = an estimate of the number of clients that will be
77                               registered. Used to preallocate the list of timed
78                               out clients
79 
80     ***************************************************************************/
81 
82     public this ( scope UnregisterDg unregister, EpollException e,
83                   ITimeoutManager timeout_manager, uint num_clients = 0 )
84     {
85         super(unregister, e);
86 
87         this.timeout_manager = timeout_manager;
88         this.timed_out_clients = new TimedOutClientList(num_clients);
89     }
90 
91     /***************************************************************************
92 
93         Handles the clients in selected_set that did not time out, then reports
94         a timeout to the timed out clients and unregisters them.
95 
96         Note that any timed out clients will *not* be handled, even if they have
97         an event fired. Instead they are finalized with status = timeout and
98         unregistered.
99 
100         Params:
101             selected_set = the result list of epoll_wait()
102             unhandled_exception_hook = if not null, will be called each time
103                 event call results in unhandled exception. May both rethrow
104                 and consume exception instance after processing it.
105 
106     ***************************************************************************/
107 
108     public override void opCall ( epoll_event_t[] selected_set,
109         scope bool delegate (Exception) unhandled_exception_hook )
110     {
111         if (this.timeout_manager.us_left < timeout_manager.us_left.max)
112         {
113             this.timeout_manager.checkTimeouts((ITimeoutClient timeout_client)
114             {
115                 auto client = cast (ISelectClient)timeout_client;
116 
117                 verify(client !is null, "timeout client is not a select client");
118 
119                 debug ( ISelectClient )
120                     Stderr.formatln("{} :: Client timed out, unregistering", client).flush();
121 
122                 this.timed_out_clients ~= client;
123 
124                 return true;
125             });
126 
127             auto timed_out_clients = this.timed_out_clients[];
128 
129             if (timed_out_clients.length)
130             {
131                 /*
132                  * Handle the clients in the selected set that didn't time out.
133                  * To do so, look up every timed out client in the selected set
134                  * using bsearch and handle it if it didn't time.
135                  * Using bsearch requires the list to be sorted.
136                  */
137 
138                 qsort(timed_out_clients.ptr, timed_out_clients.length,
139                       timed_out_clients[0].sizeof, &cmpPtr!(false));
140 
141                 foreach (key; selected_set)
142                 {
143                     ISelectClient client = cast (ISelectClient) key.data.ptr;
144 
145                     verify(client !is null);
146 
147                     const(void)* typed_ptr = timed_out_clients.ptr;
148                     if (!bsearch(cast (void*) client, typed_ptr,
149                                  timed_out_clients.length, timed_out_clients[0].sizeof, &cmpPtr!(true)))
150                     {
151                         this.handleSelectedKey(key, unhandled_exception_hook);
152                     }
153                 }
154 
155                 foreach ( client; this.timed_out_clients[] )
156                 {
157                     this.unregisterAndFinalize(client, client.FinalizeStatus.Timeout);
158                 }
159 
160                 this.timed_out_clients.clear();
161 
162                 /*
163                  * The selected set and the timed out clients are handled:
164                  * We're done.
165                  */
166 
167                 return;
168             }
169 
170             /*
171              * No client timed out: Handle the selected set normally.
172              */
173         }
174 
175         super.opCall(selected_set, unhandled_exception_hook);
176     }
177 
178     /***************************************************************************
179 
180         Compares the pointer referred to by a_ to that referred to by b_.
181 
182         Params:
183             searching = false: a_ points to the pointer to compare (called from
184                         qsort()), true: a_ is the pointer to compare (called
185                         from bsearch).
186             a_ = either the pointer (searching = true) or a pointer to the
187                  pointer (searching = false) to compare against the pointer
188                  pointed to by b_
189             b_ = pointer to the pointer to compare against a_ or the pointer a_
190                  points to
191 
192         Returns:
193             a value greater 0 if a_ compares greater than b_, a value less than
194             0 if less or 0 if a_ and b_ compare equal.
195 
196     ***************************************************************************/
197 
198     extern (C) private static int cmpPtr ( bool searching ) (
199         scope const(void*) a_,
200         scope const(void*) b_ )
201     {
202         static if (searching)
203         {
204             alias a_ a;
205         }
206         else
207         {
208             void* a = *cast (void**) a_;
209         }
210 
211         void* b = *cast (void**) b_;
212 
213         return (a >= b)? a > b : -1;
214     }
215 }