976d3ed7a7dca75c6c646f17dd362ce370964744
[libasyncio] / asyncio / async_callout.cpp
1 /**
2  * @file
3  *
4  * @copyright © Copyright 2008 by Intra2net AG
5  * @license LGPL
6  * 
7  * info@intra2net.com
8  */
9
10 #include "async_callout.hpp"
11
12 #include <tracefunc.hpp>
13 #include <logfunc.hpp>
14
15 #include <map>
16
17
18 namespace AsyncIo
19 {
20
21 namespace
22 {
23
24 typedef boost::shared_ptr< Detail::Caller > CallerPtr;
25
26 typedef std::map< unsigned long, CallerPtr > CallMap;
27
28
29 unsigned long l_last_id=0;
30
31 CallMap l_call_map;
32
33
34 /**
35  * @brief creates a new id value for a call out.
36  * @return the new id.
37  *
38  * The id value is basically just a counter.
39  * It is implemented in a way that it can not be 0 and can deal with wrap arounds.
40  */
41 unsigned long create_call_out_id_value()
42 {
43    while ( l_call_map.find(++l_last_id) != l_call_map.end() and l_last_id != 0);
44    return l_last_id;
45 } // eo create_call_out_id_value
46
47
48 void add_call( CallerPtr caller )
49 {
50    if (caller->joinId())
51    {
52       l_call_map[ caller->getCallOutId().getValue() ] = caller;
53    }
54 } // eo add_call
55
56
57 bool remove_call( unsigned long id_value )
58 {
59    CallMap::iterator it= l_call_map.find(id_value);
60    if (it != l_call_map.end())
61    {
62       l_call_map.erase(it);
63       return true;
64    }
65    return false;
66 } // eo remove_call(unsigned long)
67
68
69 CallerPtr get_call(unsigned long id_value)
70 {
71    CallMap::iterator it= l_call_map.find(id_value);
72    if (it != l_call_map.end())
73    {
74       return it->second;
75    }
76    return CallerPtr();
77 } // eo get_call(unsigned long)
78
79
80 bool has_call( unsigned long id_value )
81 {
82    CallMap::iterator it= l_call_map.find(id_value);
83    return (it != l_call_map.end() );
84 } // eo has_call(unsigned long)
85
86
87
88 } // eo namespace <anonymous>
89
90
91
92 /*
93 ** implementation of class CallOutId
94 */
95
96 CallOutId::CallOutId()
97 : m_value(0)
98 {
99 } // eo CallOutId::CallOutId()
100
101
102 CallOutId::CallOutId(unsigned long value)
103 : m_value(value)
104 {
105 } // eo CallOutId::CallOutId(unsigned long)
106
107
108 bool CallOutId::thaw() const
109 {
110    if (m_caller_weak_ptr.expired())
111    {
112       return false;
113    }
114    CallerPtr call_ptr= get_call(m_value);
115    if (call_ptr)
116    {
117       return call_ptr->thaw();
118    }
119    return false;
120 } // eo CallOutId::thaw() const
121
122
123 bool CallOutId::remove()
124 {
125    if (m_caller_weak_ptr.expired())
126    {
127       return false;
128    }
129    unsigned long value= m_value;
130    m_value= 0;
131    return remove_call(value);
132 } // eo CallOutId::remove()
133
134
135 bool CallOutId::active() const
136 {
137    return m_value!=0 and not m_caller_weak_ptr.expired() and has_call(m_value);
138 } // eo CallOutId::active() const
139
140
141 /**
142  * @brief retruns if the call is frozen.
143  * @return @a true iff the call is frozen.
144  */
145 bool CallOutId::frozen() const
146 {
147     CallerPtr caller= m_caller_weak_ptr.lock();
148     if (not caller)
149     {
150         return false;
151     }
152     return caller->frozen();
153 } // eo CallOutId::frozen() const
154
155
156 /**
157  * @brief returns the remaining time until the call is done or thrown away (on frozen calls).
158  * @return the remaining time.
159  *
160  * The result only makes sense if the call is still active.
161  */
162 MilliTime CallOutId::remaining_time()
163 {
164     CallerPtr caller= m_caller_weak_ptr.lock();
165     if ( not active() or not caller )
166     {
167         return MilliTime();
168     }
169     MilliTime t;
170     get_current_monotonic_time(t);
171     MilliTime result= caller->getWhenTime();
172     result-= t;
173     MilliTime t_null;
174     return (result < t_null ? t_null : result);
175 } // eo CallOutId::remaining_time()
176
177
178 namespace Detail
179 {
180
181 /*
182 ** implementation of class Caller
183 */
184
185 Caller::Caller( boost::function< void() > f, long delta_sec, long delta_msec, bool frozen)
186 : TimerBase()
187 , m_call_out_id( create_call_out_id_value() )
188 , m_func(f)
189 , m_waiting(frozen)
190 {
191    SCOPETRACKER();
192    setDeltaWhenTime( delta_sec, delta_msec);
193 } // eo Caller::Caller(boost::function< void() >,long)
194
195
196 Caller::~Caller()
197 {
198    SCOPETRACKER();
199 } // eo Caller::~Caller()
200
201
202 void Caller::execute()
203 {
204    SCOPETRACKER();
205    // NOTE: since the func may throw an exception, we first get a shared pointer
206    // (to prevent early deletion) and then we remove us from the call map.
207    CallerPtr ptr= shared_from_this();
208    m_call_out_id.remove();
209
210    if (m_func and not m_waiting)
211    {
212       m_func(); // may throw..., but at this point it doesn't harm.
213       //( it may harm at other places,...)
214    }
215 } // eo Caller::execute()
216
217
218 bool Caller::thaw()
219 {
220    if (m_waiting)
221    {
222       m_waiting= false;
223       setDeltaWhenTime( 0, 0 );
224       return true;
225    }
226    return false;
227 } // eo Caller::thaw()
228
229
230 bool Caller::joinId()
231 {
232    if (m_call_out_id.m_caller_weak_ptr.expired())
233    {
234       m_call_out_id.m_caller_weak_ptr= shared_from_this();
235       activate();
236       return true;
237    }
238    return false;
239 } // eo Caller::joinId()
240
241
242 bool Caller::frozen() const
243 {
244     return m_waiting;
245 } // eo Caller::frozen() const
246
247
248 } // eo namespace Detail
249
250
251
252 /**
253  * @brief remove a pending call by id.
254  *
255  * @param id the call id which should be removed.
256  * @return @a true iff the call was removed, @a false if no call with the given id was found.
257  */
258 bool removeCallOut( CallOutId& id )
259 {
260    return id.remove();
261 } // eo removeCallOut(CallOutId&)
262
263
264
265 template<>
266 CallOutId callOut( boost::function< void() > f, long delta_sec)
267 {
268    CallerPtr caller( new Detail::Caller(f,delta_sec) );
269    add_call(caller);
270    return caller->getCallOutId();
271 } // eo callOut(boost::function< void() >,long)
272
273 template<>
274 CallOutId callOut( boost::function< void() > f, int delta_sec)
275 {
276    return callOut<long>(f,delta_sec);
277 } // eo callOut(boost::function< void() >,int)
278
279
280 template<>
281 CallOutId callOut( boost::function< void() > f, double delta_sec )
282 {
283    long delta_sec_i = (long)delta_sec;
284    long delta_sec_m = (long)((delta_sec - (double)delta_sec_i)*1000.0);
285    CallerPtr caller( new Detail::Caller(f,delta_sec_i, delta_sec_m) );
286    add_call(caller);
287    return caller->getCallOutId();
288 } // eo callOut(boost::function< void() >,double)
289
290
291 template<>
292 CallOutId callOut( boost::function< void() > f, float delta_sec )
293 {
294    return callOut<double>(f,delta_sec);
295 } // eo callOut(boost::function< void() >,float)
296
297
298
299
300 template<>
301 CallOutId frozenCall( boost::function< void() > f, long delta_sec)
302 {
303    CallerPtr caller( new Detail::Caller(f,delta_sec, 0, true) );
304    add_call(caller);
305    return caller->getCallOutId();
306 } // eo frozenCall(boost::function< void() >,long)
307
308 template<>
309 CallOutId frozenCall( boost::function< void() > f, int delta_sec)
310 {
311    return frozenCall<long>(f,delta_sec);
312 } // eo frozenCall(boost::function< void() >,int)
313
314
315 template<>
316 CallOutId frozenCall( boost::function< void() > f, double delta_sec )
317 {
318    long delta_sec_i = (long)delta_sec;
319    long delta_sec_m = (long)((delta_sec - (double)delta_sec_i)*1000.0);
320    CallerPtr caller( new Detail::Caller(f,delta_sec_i, delta_sec_m, true) );
321    add_call(caller);
322    return caller->getCallOutId();
323 } // eo frozenCall(boost::function< void() >,double)
324
325
326 template<>
327 CallOutId frozenCall( boost::function< void() > f, float delta_sec )
328 {
329    return frozenCall<double>(f,delta_sec);
330 } // eo frozenCall(boost::function< void() >,float)
331
332
333 } // eo namespace AsyncIo