Migrate libasyncio from boost.signal to signals2 (#8756)
[libasyncio] / asyncio / async_callout.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /**
21  * @file
22  *
23  * @copyright © Copyright 2008-2009 by Intra2net AG
24  * @contact opensource@intra2net.com
25  *
26  */
27 #include "async_callout.hpp"
28 #include <asyncio_config.hpp>
29
30 #ifdef HAVE_LIBI2NCOMMON
31 #include <tracefunc.hpp>
32 #include <logfunc.hpp>
33 #else
34 #define SCOPETRACKER(x) do{}while(false)
35 #endif
36
37 #include <map>
38
39
40 namespace AsyncIo
41 {
42
43 // anonymous namespace for our secret details :-)
44 namespace
45 {
46
47 typedef boost::shared_ptr< Detail::Caller > CallerPtr;
48
49 typedef std::map< unsigned long, CallerPtr > CallMap;
50
51
52 unsigned long l_last_id=0;
53
54 CallMap l_call_map;
55
56
57 /**
58  * @brief creates a new id value for a call out.
59  * @return the new id.
60  *
61  * The id value is basically just a counter.
62  * It is implemented in a way that it can not be 0 and can deal with wrap arounds.
63  */
64 unsigned long create_call_out_id_value()
65 {
66    while ( l_call_map.find(++l_last_id) != l_call_map.end() and l_last_id != 0);
67    return l_last_id;
68 } // eo create_call_out_id_value
69
70
71 /**
72  * @brief add a caller instance to the local call map.
73  * @param caller the caller instance.
74  */
75 void add_call( CallerPtr caller )
76 {
77    if (caller->joinId())
78    {
79       l_call_map[ caller->getCallOutId().getValue() ] = caller;
80    }
81 } // eo add_call
82
83
84 /**
85  * @brief removes an entry from the local call map
86  * @param id_value id to remove from the map.
87  * @return @a true if an entry was found and removed
88  *  @a false if entry was not found in the map.
89  */
90 bool remove_call( unsigned long id_value )
91 {
92    CallMap::iterator it= l_call_map.find(id_value);
93    if (it != l_call_map.end())
94    {
95       l_call_map.erase(it);
96       return true;
97    }
98    return false;
99 } // eo remove_call(unsigned long)
100
101
102 /**
103  * @brief return the caller for an id.
104  * @param id_value the id to search the caller for.
105  * @return caller; empty pointer if not found.
106  */
107 CallerPtr get_call(unsigned long id_value)
108 {
109    CallMap::iterator it= l_call_map.find(id_value);
110    if (it != l_call_map.end())
111    {
112       return it->second;
113    }
114    return CallerPtr();
115 } // eo get_call(unsigned long)
116
117
118 /**
119  * @brief tests if an id can be found in the call map.
120  * @param id_value the id to search for.
121  * @return @a true if the id is found in the call map.
122  */
123 bool has_call( unsigned long id_value )
124 {
125    CallMap::iterator it= l_call_map.find(id_value);
126    return (it != l_call_map.end() );
127 } // eo has_call(unsigned long)
128
129
130
131 } // eo namespace <anonymous>
132
133
134
135 /*
136 ** implementation of class CallOutId
137 */
138
139 CallOutId::CallOutId()
140 : m_value(0)
141 {
142 } // eo CallOutId::CallOutId()
143
144
145 CallOutId::CallOutId(unsigned long value)
146 : m_value(value)
147 {
148 } // eo CallOutId::CallOutId(unsigned long)
149
150
151 /**
152  * @brief thaws (activate) the referenced (and frozen) call.
153  * @return @a true if the call was successfully thawed; @a false else.
154  *
155  * A call (referenced the id instance) can be successfully thawed if:
156  * - the call was a frozen call
157  * - the call is still existing; i.e. we are within the given time period
158  *   for that call.
159  * .
160  */
161 bool CallOutId::thaw() const
162 {
163    if (m_caller_weak_ptr.expired())
164    {
165       return false;
166    }
167    CallerPtr call_ptr= get_call(m_value);
168    if (call_ptr)
169    {
170       return call_ptr->thaw();
171    }
172    return false;
173 } // eo CallOutId::thaw() const
174
175
176 /**
177  * @brief removes the referenced call.
178  * @return @a true if the call was removed; @a false else.
179  *
180  * @note after calling this method, the refernced call is not existing.
181  * Either it was removed or it was already removed earlier (explicit or implicit).
182  */
183 bool CallOutId::remove()
184 {
185    if (m_caller_weak_ptr.expired())
186    {
187       return false;
188    }
189    unsigned long value= m_value;
190    m_value= 0;
191    return remove_call(value);
192 } // eo CallOutId::remove()
193
194
195 /**
196  * @brief returns if the referenced call is still active.
197  * @return @a true if the referenced call is active.
198  *
199  * The referenced call is active if it is still waiting to be executed.
200  */
201 bool CallOutId::active() const
202 {
203    return m_value!=0 and not m_caller_weak_ptr.expired() and has_call(m_value);
204 } // eo CallOutId::active() const
205
206
207 /**
208  * @brief returns if the call is frozen.
209  * @return @a true iff the call is frozen.
210  */
211 bool CallOutId::frozen() const
212 {
213     CallerPtr caller= m_caller_weak_ptr.lock();
214     if (not caller)
215     {
216         return false;
217     }
218     return caller->frozen();
219 } // eo CallOutId::frozen() const
220
221
222 /**
223  * @brief returns the remaining time until the call is done or thrown away (on frozen calls).
224  * @return the remaining time.
225  *
226  * The result only makes sense if the call is still active.
227  *
228  * If the references call is not active then the returned time is 0.
229  */
230 MilliTime CallOutId::remaining_time()
231 {
232     CallerPtr caller= m_caller_weak_ptr.lock();
233     if ( not active() or not caller )
234     {
235         return MilliTime();
236     }
237     MilliTime t;
238     get_current_monotonic_time(t);
239     MilliTime result= caller->getWhenTime();
240     result-= t;
241     MilliTime t_null;
242     return (result < t_null ? t_null : result);
243 } // eo CallOutId::remaining_time()
244
245
246 namespace Detail
247 {
248
249 /*
250 ** implementation of class Caller
251 */
252
253 Caller::Caller( boost::function< void() > f, long delta_sec, long delta_msec, bool frozen)
254 : TimerBase()
255 , m_call_out_id( create_call_out_id_value() )
256 , m_func(f)
257 , m_waiting(frozen)
258 {
259    SCOPETRACKER();
260    setDeltaWhenTime( delta_sec, delta_msec);
261 } // eo Caller::Caller(boost::function< void() >,long)
262
263
264 Caller::~Caller()
265 {
266    SCOPETRACKER();
267 } // eo Caller::~Caller()
268
269
270 void Caller::execute()
271 {
272    SCOPETRACKER();
273    // NOTE: since the func may throw an exception, we first get a shared pointer
274    // (to prevent early deletion) and then we remove us from the call map.
275    CallerPtr ptr= shared_from_this();
276    m_call_out_id.remove();
277
278    if (m_func and not m_waiting)
279    {
280       m_func(); // may throw..., but at this point it doesn't harm.
281       //( it may harm at other places,...)
282    }
283 } // eo Caller::execute()
284
285
286 bool Caller::thaw()
287 {
288    if (m_waiting)
289    {
290       m_waiting= false;
291       setDeltaWhenTime( 0, 0 );
292       return true;
293    }
294    return false;
295 } // eo Caller::thaw()
296
297
298 bool Caller::joinId()
299 {
300    if (m_call_out_id.m_caller_weak_ptr.expired())
301    {
302       m_call_out_id.m_caller_weak_ptr= shared_from_this();
303       activate();
304       return true;
305    }
306    return false;
307 } // eo Caller::joinId()
308
309
310 bool Caller::frozen() const
311 {
312     return m_waiting;
313 } // eo Caller::frozen() const
314
315
316 } // eo namespace Detail
317
318
319
320 /**
321  * @brief remove a pending call by id.
322  *
323  * @param id the call id which should be removed.
324  * @return @a true iff the call was removed, @a false if no call with the given id was found.
325  */
326 bool removeCallOut( CallOutId& id )
327 {
328    return id.remove();
329 } // eo removeCallOut(CallOutId&)
330
331
332
333 template<>
334 CallOutId callOut( boost::function< void() > f, long delta_sec)
335 {
336    CallerPtr caller( new Detail::Caller(f,delta_sec) );
337    add_call(caller);
338    return caller->getCallOutId();
339 } // eo callOut(boost::function< void() >,long)
340
341 template<>
342 CallOutId callOut( boost::function< void() > f, int delta_sec)
343 {
344    return callOut<long>(f,delta_sec);
345 } // eo callOut(boost::function< void() >,int)
346
347
348 template<>
349 CallOutId callOut( boost::function< void() > f, double delta_sec )
350 {
351    long delta_sec_i = (long)delta_sec;
352    long delta_sec_m = (long)((delta_sec - (double)delta_sec_i)*1000.0);
353    CallerPtr caller( new Detail::Caller(f,delta_sec_i, delta_sec_m) );
354    add_call(caller);
355    return caller->getCallOutId();
356 } // eo callOut(boost::function< void() >,double)
357
358
359 template<>
360 CallOutId callOut( boost::function< void() > f, float delta_sec )
361 {
362    return callOut<double>(f,delta_sec);
363 } // eo callOut(boost::function< void() >,float)
364
365
366
367
368 template<>
369 CallOutId frozenCall( boost::function< void() > f, long delta_sec)
370 {
371    CallerPtr caller( new Detail::Caller(f,delta_sec, 0, true) );
372    add_call(caller);
373    return caller->getCallOutId();
374 } // eo frozenCall(boost::function< void() >,long)
375
376 template<>
377 CallOutId frozenCall( boost::function< void() > f, int delta_sec)
378 {
379    return frozenCall<long>(f,delta_sec);
380 } // eo frozenCall(boost::function< void() >,int)
381
382
383 template<>
384 CallOutId frozenCall( boost::function< void() > f, double delta_sec )
385 {
386    long delta_sec_i = (long)delta_sec;
387    long delta_sec_m = (long)((delta_sec - (double)delta_sec_i)*1000.0);
388    CallerPtr caller( new Detail::Caller(f,delta_sec_i, delta_sec_m, true) );
389    add_call(caller);
390    return caller->getCallOutId();
391 } // eo frozenCall(boost::function< void() >,double)
392
393
394 template<>
395 CallOutId frozenCall( boost::function< void() > f, float delta_sec )
396 {
397    return frozenCall<double>(f,delta_sec);
398 } // eo frozenCall(boost::function< void() >,float)
399
400
401 } // eo namespace AsyncIo