source: trash/old-modules/transport/protlib/timer_module.cpp@ 9662

Last change on this file since 9662 was 5641, checked in by Christoph Mayer, 15 years ago
File size: 18.9 KB
Line 
1/// ----------------------------------------*- mode: C++; -*--
2/// @file timer_module.cpp
3/// timer module that maintains timers and triggers messages
4/// ----------------------------------------------------------
5/// $Id: timer_module.cpp 2756 2007-08-06 12:51:39Z bless $
6/// $HeadURL: https://svn.ipv6.tm.uka.de/nsis/protlib/trunk/src/timer_module.cpp $
7// ===========================================================
8//
9// Copyright (C) 2005-2007, all rights reserved by
10// - Institute of Telematics, Universitaet Karlsruhe (TH)
11//
12// More information and contact:
13// https://projekte.tm.uka.de/trac/NSIS
14//
15// This program is free software; you can redistribute it and/or modify
16// it under the terms of the GNU General Public License as published by
17// the Free Software Foundation; version 2 of the License
18//
19// This program is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22// GNU General Public License for more details.
23//
24// You should have received a copy of the GNU General Public License along
25// with this program; if not, write to the Free Software Foundation, Inc.,
26// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27//
28// ===========================================================
29/** @ingroup timermodule
30 * The timer module provides a way for other modules to set timers via a
31 * message and receive a message back on their queue when the timer goes
32 * off.
33 */
34
35#include <sstream>
36
37#include "timer_module.h"
38#include "queuemanager.h"
39#include "logfile.h"
40#include "cleanuphandler.h"
41
42namespace protlib {
43
44/** @addtogroup timermodule Timer Module
45 * @{
46 */
47
48 using namespace log;
49
50/***** class TimerMsg *****/
51
52TimerMsg::TimerMsg(qaddr_t s, bool s_err)
53 : message(type_timer,s),
54 time_sec(0),
55 time_msec(0),
56 action(ac_ignore),
57 param1(NULL),
58 param2(NULL),
59 ok(true),
60 send_error(s_err),
61 relative(true)
62{
63 // Log(DEBUG_LOG,LOG_NORMAL,"TimerMsg","TimerMsg constructor. msgid:" << get_id());
64} // end TimerMsg
65
66/** Destructor, does nothing. */
67TimerMsg::~TimerMsg() {}
68
69
70/** Set result flag and get old value.
71 * You should not set this, this is done by the timer module.
72 */
73bool TimerMsg::set_ok(bool r) {
74 register bool old = ok;
75 ok = r;
76 return old;
77} // end set_ok
78
79/** Prepare message to start an absolute timer. */
80bool TimerMsg::start_absolute(int32 sec, int32 msec, param_t p1, param_t p2) {
81 return start(false,sec,msec,p1,p2);
82} // end start_absolute
83
84/** Prepare message to start a relative timer. */
85bool TimerMsg::start_relative(int32 sec, int32 msec, param_t p1, param_t p2) {
86 return start(true,sec,msec,p1,p2);
87} // end start_relative
88
89bool TimerMsg::start(bool rel, int32 sec, int32 msec, param_t p1, param_t p2) {
90 time_sec = (sec<=0)?0:sec;
91 time_msec = (msec<=0)?0:msec;
92 param1 = p1;
93 param2 = p2;
94 action = ac_start;
95 relative = rel;
96 ok = true;
97 return true;
98} // end start
99
100/** Restart an absolute timer. */
101bool TimerMsg::restart_absolute(id_t id, int32 sec, int32 msec) {
102 return restart(false,id,sec,msec);
103} // end restart_absolute
104
105/** Restart a relative timer. */
106bool TimerMsg::restart_relative(id_t id, int32 sec, int32 msec) {
107 return restart(true,id,sec,msec);
108} // end restart_relative
109
110/** restart timer
111 * please note that this method actually overwrites the id of the message(!)
112 */
113bool TimerMsg::restart(bool rel, id_t id, int32 sec, int32 msec) {
114 relative = rel;
115 if (id && set_id(id)) {
116 time_sec = (sec<=0)?0:sec;
117 time_msec = (msec<=0)?0:msec;
118 action = ac_restart;
119 ok = true;
120 } else {
121 time_sec = 0;
122 time_msec = 0;
123 action = ac_ignore;
124 ok = false;
125 } // end if id
126 return ok;
127} // end restart
128
129/** Stop a timer. */
130bool TimerMsg::stop(id_t id) {
131 relative = false;
132 time_sec = 0;
133 time_msec = 0;
134 param1 = param2 = NULL;
135 if (id && set_id(id)) {
136 action = ac_stop;
137 ok = true;
138 } else {
139 action = ac_ignore;
140 ok = false;
141 } // end if id
142 return ok;
143} // end stop
144
145/** Stop all running timers. */
146bool TimerMsg::stop_all() {
147 relative = false,
148 time_sec = 0;
149 time_msec = 0;
150 param1 = param2 = NULL;
151 action = ac_stop_all;
152 ok = true;
153 return true;
154} // end stop_all
155
156/** Prepare message for sending it back as a reply message from the
157 * TimerModule when its timer expires.
158 */
159bool TimerMsg::set_elapsed() {
160 send_error = false;
161 action = ac_elapsed;
162 ok = true;
163 return ok;
164} // end set_elapsed
165
166bool TimerMsg::get_send_error() const {
167 return send_error;
168} // end get_send_error
169
170/** Set send_error flag and return old value. */
171bool TimerMsg::set_send_error(bool f) {
172 register bool o = send_error;
173 send_error = f;
174 return o;
175} // end set_send_error
176
177TimerMsg::param_t TimerMsg::get_param1() const { return param1; }
178
179TimerMsg::param_t TimerMsg::get_param2() const { return param2; }
180
181void TimerMsg::get_params(param_t& p1, param_t& p2) const {
182 p1 = param1;
183 p2 = param2;
184} // end get_params
185
186bool TimerMsg::is_absolute() const { return (!relative); }
187
188bool TimerMsg::is_relative() const { return relative; }
189
190/***** struct TimerModuleParam *****/
191
192/** @param sleep_time default sleep time
193 * @param sua send messages until aborted or just until stopped
194 * @param see send error messages as expedited data
195 * @param sre send reply messages as expedited data
196 */
197TimerModuleParam::TimerModuleParam(uint32 sleep_time, bool sua, bool see, bool sre)
198 : ThreadParam(sleep_time, "TimerModule", 2), send_until_abort(sua),
199 source(message::qaddr_timer),
200 send_error_expedited(see), send_reply_expedited(sre) {
201 // nothing more to do
202} // end constructor TimerModuleParam
203
204/***** class TimerModule *****/
205
206/** Set parameters. */
207TimerModule::TimerModule(const TimerModuleParam& p)
208 : Thread(p), timerparam(p) {
209 tmap.clear();
210 // register queue
211 QueueManager::instance()->register_queue(get_fqueue(),p.source);
212 DLog(timerparam.name, "Creating TimerModule object");
213} // end constructor TimerModule
214
215/** Stop all running timers. */
216TimerModule::~TimerModule() {
217 stop_all_timers();
218 DLog(timerparam.name, "Destroying TimerModule object");
219 QueueManager::instance()->unregister_queue(timerparam.source);
220
221} // end destructor TimerModule
222
223/** Devide Threads in thos which process the queue and those which process
224 * expired timers.
225 */
226void TimerModule::main_loop(uint32 nr) {
227 Log(INFO_LOG,LOG_NORMAL, timerparam.name, "Starting " << timerparam.name << " thread #" << nr << ", " << ((nr%2) ? "processing input queue" : "processing timer callbacks"));
228
229 if (nr%2) process_queue();
230 else process_elapsed_timers();
231
232 Log(INFO_LOG,LOG_NORMAL, timerparam.name,"Thread #" << nr << " stopped");
233} // end main_loop
234
235/** Wait for incomming mesages and evaluate message action.
236 * Messages are accepted until the module is asked to stop.
237 *
238 * The module mutex inherited from the Thread base class is locked here, so all
239 * called member functions are called inside a critical section if necessary.
240 */
241void TimerModule::process_queue() {
242 uint32 wait = timerparam.sleep_time*1000;
243 message* msg = NULL;
244 TimerMsg* tmsg = NULL;
245 FastQueue* fq = QueueManager::instance()->get_queue(message::qaddr_timer);
246 bool opresult = false;
247 if (!fq) {
248 Log(ERROR_LOG,LOG_ALERT, timerparam.name," cannot find input queue");
249 return;
250 } // end if not fq
251 // wait for messages
252 while (get_state()==STATE_RUN) {
253 msg = fq->dequeue_timedwait(wait);
254 if (msg) {
255 if (msg->get_type()==message::type_timer) {
256 tmsg = dynamic_cast<TimerMsg*>(msg);
257 if (tmsg) {
258 // begin critical section
259 lock(); // install_cleanup_thread_lock(TimerModule,this);
260 if (tmsg->is_ok()) {
261 // evaluate action
262 switch (tmsg->get_action()) {
263 case TimerMsg::ac_ignore:
264 // do nothing
265 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"received message with action set to ignore");
266 opresult = true;
267 break;
268 case TimerMsg::ac_start:
269 opresult = start_timer(tmsg);
270 break;
271 case TimerMsg::ac_restart:
272 opresult = restart_timer(tmsg);
273 break;
274 case TimerMsg::ac_stop:
275 opresult = stop_timer(tmsg);
276 break;
277 case TimerMsg::ac_stop_all:
278 opresult = stop_all_timers();
279 break;
280 default:
281 ERRLog(timerparam.name, "Wrong action " << tmsg->get_action() << " in message from " << tmsg->get_qaddr_name() << " to " << message::get_qaddr_name(timerparam.source) );
282
283 opresult = false;
284 } // end switch get_action
285 } else {
286 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"received message in invalid state, mid " << tmsg->get_id() );
287 opresult = false;
288 } // if tmsg->is_ok()
289 // error handling, message disposing
290 send_error_or_dispose(tmsg,opresult);
291 // end critical section
292 unlock(); // uninstall_cleanup(1);
293 } else {
294 Log(ERROR_LOG,LOG_ALERT, timerparam.name, "Cannot cast message from " << msg->get_qaddr_name() << " of type " << msg->get_type_name() << " to TimerMsg");
295 delete msg;
296 } // end if tmsg
297 } else {
298 ERRLog(timerparam.name,"received message that is not of type_timer from " << msg->get_qaddr_name() << ", type was " << msg->get_type_name());
299 delete msg;
300 } // end if type
301 } // end if msg
302 } // end while running
303} // end process_queue
304
305/** Check if timers expired and send reply messages.
306 * Reply messages are sent until the module is asked to abort if
307 * the flag send_until_abort is set true. Otherwise no messages are sent
308 * after a stop request.
309 */
310void TimerModule::process_elapsed_timers() {
311 state_t end_state;
312 uint32 num = 0;
313 uint32 sleeptime = timerparam.sleep_time*1000;
314 if (timerparam.send_until_abort) end_state = STATE_ABORT;
315 else end_state = STATE_STOP;
316 while(get_state()!=end_state) {
317 num = tm.check_timers_wait(sleeptime);
318 if (num) {
319 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"got " << num << " elapsed timer(s)");
320 } // end if num
321 } // end while state
322} // end process_elapsed_timers
323
324/** Starts a timer and stores its ID and the reply message in the hash maps. */
325bool TimerModule::start_timer(TimerMsg* m) {
326 timer_id_t tid = 0;
327 bool res = true;
328 message::id_t mid = m->get_id();
329 bool relative = m->is_relative();
330 int32 sec = 0;
331 int32 msec = 0;
332 if (mid) {
333 // lookup timer ID in map
334 if ((tid=tmap.lookup_tid(mid))) {
335 ERRLog(timerparam.name, m->get_qaddr_name() << " tried to start a timer with mid " << mid << ", but there is already a timer " << tid);
336 res = false;
337 } else {
338 // start timer
339 m->get_time(sec,msec);
340 if (relative) tid = tm.start_relative(this,sec,msec,NULL);
341 else tid = tm.start_absolute(this,sec,msec,NULL);
342 if (tid) {
343 // insert in map
344 tmap.insert(tid,m);
345 // timer successfully started
346 Log(EVENT_LOG,LOG_UNIMP, timerparam.name, "Timer " << tid << " (" << sec << "s " << msec << "ms) started for "
347 << m->get_qaddr_name() << " with mid " << mid);
348 res = true;
349 } else {
350 // timer not started
351 ERRLog(timerparam.name, "TimerManager in " << timerparam.name << " is unable to start a timer for " << m->get_qaddr_name());
352 res = false;
353 } // end if tid
354 } // end if lookup repmsg
355 } else {
356 ERRLog(timerparam.name, m->get_qaddr_name() << " tried to start a timer with message ID 0");
357 res = false;
358 } // end if repmsg
359 return res;
360} // end start_timer
361
362/** Restarts a timer and stores its ID and the reply message in the hash maps. */
363bool TimerModule::restart_timer(TimerMsg* m) {
364 timer_id_t tid = 0;
365 bool res = true;
366 message::id_t mid = m->get_id();
367 bool relative = m->is_relative();
368 TimerMsg* repmsg = NULL;
369 int32 sec = 0;
370 int32 msec = 0;
371 if (mid) {
372 // lookup timer_id and reply message for mid in map
373 tid = tmap.lookup_tid(mid);
374 repmsg = tmap.lookup_msg(tid);
375 if (tid && repmsg) {
376 // restart timer
377 m->get_time(sec,msec);
378 if (relative) res = tm.restart_relative(tid,sec,msec);
379 else res = tm.restart_absolute(tid,sec,msec);
380 if (res) {
381 // modify reply message
382 repmsg->restart(relative,mid,sec,msec);
383 // timer successfully restarted
384 DLog(timerparam.name, "Timer " << tid << ", mid " << mid << " restarted for " << m->get_qaddr_name());
385 } else {
386 // timer not restarted
387 ERRLog(timerparam.name, "TimerManager in " << timerparam.name << " is unable to restart a timer for " << m->get_qaddr_name() << ", mid " << mid);
388 } // end if res
389 } else {
390 if (tid) ERRLog(timerparam.name, m->get_qaddr_name() << " tried to restart a timer with mid " << mid << ": or no reply message for timer found");
391 if (repmsg) ERRLog(timerparam.name, m->get_qaddr_name() << " tried to restart timer with mid " << mid << ": timer not found");
392 if ((!repmsg) & (!tid)) ERRLog(timerparam.name, m->get_qaddr_name() << " tried to restart timer with mid " << mid << ": neither timer nor reply message found");
393 res = false;
394 } // end if tid
395 } else {
396 ERRLog(timerparam.name, m->get_qaddr_name() << " tried to restart a timer with an invalid message ID");
397 res = false;
398 } // end if repmsg
399 return res;
400} // end restart_timer
401
402/** Stop a timer and remove its ID and reply message from the hash maps. */
403bool TimerModule::stop_timer(TimerMsg* m) {
404 timer_id_t tid = 0;
405 bool res = true;
406 message::id_t mid = m->get_id();
407 if (mid) {
408 // lookup timer_id for mid in map
409 tid = tmap.lookup_tid(mid);
410 if (tid) {
411 // stop timer
412 res = tm.stop(tid);
413 if (res) {
414 // delete from map
415 tmap.erase(tid,mid,true);
416 // timer stopped
417 DLog(timerparam.name, "Stopped timer " << tid << ", mid " << mid << " for " << m->get_qaddr_name());
418 } else {
419 // timer not stopped
420 ERRLog(timerparam.name, "TimerManager in " << timerparam.name << " is unable to stop timer " << tid << ", mid " << mid << " for " << m->get_qaddr_name());
421 } // end if tid
422 } else {
423 ERRLog(timerparam.name, m->get_qaddr_name() << " tried to stop a non-existing timer with mid " << mid);
424 res = false;
425 } // end if tid
426 } else {
427 ERRLog(timerparam.name, m->get_qaddr_name() << " tried to stop a timer with an invalid message ID");
428 res = false;
429 } // end if repmsg
430 return res;
431} // end stop_timer
432
433/** Stop all timers and clear the hash maps. */
434bool TimerModule::stop_all_timers() {
435 uint32 num = 0;
436 // clear map
437 tmap.clear(true);
438 // clear TimerManager
439 num = tm.stop_all();
440 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"stopped all timers, num " << num);
441 return true;
442} // end stop_all_timers
443
444/** Send back error message.
445 * If no error is necessary, the message is disposed.
446 * @param m message that is changed to an error message if
447 * its flags are set appropriately.
448 * @param ok success or error?
449 */
450void TimerModule::send_error_or_dispose(TimerMsg* m, bool ok) {
451 message::qaddr_t dest;
452 if (!m) return;
453
454#ifndef _NO_LOGGING
455 message::id_t mid = m->get_id(); // only used for logging
456#endif
457
458 // Do we send errors?
459 if ((!ok) && m->get_send_error()) ok = false;
460 else ok = true;
461 if (ok) {
462 // dispose if not a start message
463 if (m->get_action()!=TimerMsg::ac_start) {
464 dest = m->get_source();
465 delete m;
466 //Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"disposed message " << mid << " from " << message::get_qaddr_name(dest));
467 } // end if dispose message
468 } else {
469 // send error
470 dest = m->get_source();
471 m->set_ok(false);
472 if (m->send(timerparam.source,dest,timerparam.send_error_expedited)) {
473 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"sent error message w/ mid " << mid << " to " << message::get_qaddr_name(dest));
474 } else {
475 ERRLog(timerparam.name,"cannot send error message w/ mid " << mid << " to " << message::get_qaddr_name(dest) << ", disposing it now");
476 delete m;
477 } // end if sent error
478 } // end if ok
479} // end send_error_or_dispose
480
481/** This is the callback for the TimerManager used by the TimerModule object.
482 * @param timer timer ID
483 * @param callback_param additional callback parameter.
484 *
485 * The module mutex inherited from the Thread base class is locked here, so all
486 * called member functions are called inside a critical section if necessary.
487 */
488void TimerModule::timer_expired(timer_id_t timer, timer_callback_param_t callback_param) {
489 TimerMsg* msg = NULL;
490 message::qaddr_t dest;
491 message::id_t mid = 0;
492 // begin critical section
493 lock(); // install_cleanup_thread_lock(TimerModule,this);
494 // get reply message
495 msg = tmap.lookup_msg(timer);
496 if (msg) {
497 // store message ID for erasing this record from the map
498 mid = msg->get_id();
499 // send message
500 dest = msg->get_source();
501 if (msg->set_elapsed() && msg->send_back(timerparam.source,timerparam.send_reply_expedited)) {
502 Log(DEBUG_LOG,LOG_UNIMP, timerparam.name,"sent reply mid " << mid << " for elapsed timer " << timer << " to " << message::get_qaddr_name(dest));
503 } else {
504 ERRLog(timerparam.name,"cannot send reply mid " << mid << " for elapsed timer " << timer <<" to " << message::get_qaddr_name(dest));
505 // dispose message
506 delete msg;
507 } // end if send_back
508 } else {
509 // may be timer has been stopped
510 DLog(timerparam.name, "TimerModule::timer_expired cannot find reply message for a timer " << timer << ". Maybe the timer has been stopped");
511 } // end if msg
512 // erase (timer,mid) without disposing the reply message
513 tmap.erase(timer,mid,false);
514 // end critical section
515 unlock(); // uninstall_cleanup(1);
516} // end timer_expired
517
518/***** class TimerModule::TimerMap *****/
519
520bool TimerModule::TimerMap::insert(timer_id_t tid, TimerMsg* m) {
521 if (tid && m) {
522 message::id_t mid = m->get_id();
523 tid2mid[tid] = mid;
524 mid2tid[mid] = tid;
525 tid2msg[tid] = m;
526 return true;
527 } else return false;
528} // end insert
529
530/** Returns the message ID of the timer bound to timer ID or 0 if not
531 * found, since every timer must have an ID <> 0.
532 */
533message::id_t TimerModule::TimerMap::lookup_mid(timer_id_t tid) const {
534 const_tid2mid_it_t hit;
535 hit = tid2mid.find(tid);
536 if (hit!=tid2mid.end()) return hit->second;
537 else return 0;
538} // end lookup
539
540/** Returns the timer-ID of the timer bound to the message ID or 0 if not
541 * found, since 0 is never used as a timer ID.
542 */
543timer_id_t TimerModule::TimerMap::lookup_tid(message::id_t mid) const {
544 const_mid2tid_it_t hit;
545 if ((hit=mid2tid.find(mid))!=mid2tid.end()) return hit->second;
546 else return 0;
547} // end lookup
548
549/** Returns the timer message of the timer bound to timer ID or NULL if not
550 * found.
551 */
552TimerMsg* TimerModule::TimerMap::lookup_msg(timer_id_t tid) const {
553 const_tid2msg_it_t hit;
554 hit = tid2msg.find(tid);
555 if (hit!=tid2msg.end()) return hit->second;
556 else return NULL;
557} // end lookup_msg
558
559/** Please be sure that timer ID and message ID are bound to the same timer.
560 * No checking is done!
561 */
562void TimerModule::TimerMap::erase(timer_id_t tid, message::id_t mid, bool dispose) {
563 TimerMsg* m = NULL;
564 if (tid) {
565 if (dispose) {
566 m = lookup_msg(tid);
567 if (m) delete m;
568 } // end if dispose
569 tid2mid.erase(tid);
570 tid2msg.erase(tid);
571 } // end if tid
572 if (mid) mid2tid.erase(mid);
573} // end erase
574
575void TimerModule::TimerMap::clear(bool dispose) {
576 const_tid2msg_it_t hit;
577 if (dispose) {
578 for (hit=tid2msg.begin();hit!=tid2msg.end();hit++) {
579 if (hit->second) delete hit->second;
580 } // end for hit
581 } // end if dispose
582 tid2mid.clear();
583 tid2msg.clear();
584 mid2tid.clear();
585} // end clear
586
587//@}
588
589} // end namespace protlib
Note: See TracBrowser for help on using the repository browser.