feisty meow concerns codebase  2.140
test_timer_driver.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : t_timer_driver *
4 * Author : Chris Koeritz *
5 * *
6 * Purpose: *
7 * *
8 * Tests the timer driver class from the operating system library. *
9 * *
10 *******************************************************************************
11 * Copyright (c) 2005-$now By Author. This program is free software; you can *
12 * redistribute it and/or modify it under the terms of the GNU General Public *
13 * License as published by the Free Software Foundation; either version 2 of *
14 * the License or (at your option) any later version. This is online at: *
15 * http://www.fsf.org/copyleft/gpl.html *
16 * Please send any updates to: fred@gruntose.com *
17 \*****************************************************************************/
18 
22 #include <basis/functions.h>
23 #include <basis/guards.h>
24 #include <basis/astring.h>
27 #include <loggers/file_logger.h>
28 #include <mathematics/chaos.h>
29 #include <processes/ethread.h>
31 #include <structures/set.h>
32 #include <structures/unique_id.h>
34 #include <timely/time_control.h>
35 #include <timely/time_stamp.h>
36 #include <timely/timer_driver.h>
37 #include <unit_test/unit_base.h>
38 
39 using namespace application;
40 using namespace basis;
41 using namespace filesystem;
42 using namespace loggers;
43 using namespace processes;
44 using namespace structures;
45 using namespace timely;
46 using namespace unit_test;
47 
48 #define LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger::get(), s)
49 
51  // with no arguments to the test, we'll run timers for this very short duration.
52 
53 const int MAX_THREADS = 120;
54 
56 
57 class timer_driver_tester : virtual public unit_base, virtual public application_shell
58 {
59 public:
60  timer_driver_tester()
61  : application_shell(), _in_progress(false) {}
62  DEFINE_CLASS_NAME("timer_driver_tester");
63  virtual ~timer_driver_tester() {}
64 
65  int execute();
66 
67  thread_cabinet &threads() { return _threads; }
68 
69  bool in_progress() const { return _in_progress; }
70  // returns true if activity is currently occurring on the main thread.
71  // we don't expect this activity to be interrupted by timer events.
72 
73 private:
74  bool _in_progress;
75  // simple flag to check when a timer hits. if this is true, then a timer
76  // hit while we were doing actual functional operations, rather than
77  // just waiting in a sleep.
78  thread_cabinet _threads; // storage for our time_stamp testing threads.
79 };
80 
82 
83 class timer_test_thread : public ethread
84 {
85 public:
86  timer_test_thread(application_shell &parent)
87  : ethread(parent.randomizer().inclusive(8, 25)), _parent(parent)
88  { start(NULL_POINTER); }
89 
90  DEFINE_CLASS_NAME("timer_test_thread");
91  void perform_activity(void *) {
92  FUNCDEF("perform_activity");
93  if (time_stamp() < _started)
94  deadly_error(class_name(), func, "start time is before current time.");
95  if (time_stamp() < _last)
96  deadly_error(class_name(), func, "last check is before current time.");
97  _last.reset(); // set the last time to right now.
98  time_stamp ted;
99  time_stamp jed;
100  if (ted > jed)
101  deadly_error(class_name(), func, "jed is less than test.");
102  }
103 
104 private:
105  application_shell &_parent;
106  time_stamp _started;
107  time_stamp _last;
108 };
109 
111 
112 class my_timer_handler : public timeable
113 {
114 public:
115  my_timer_handler(timer_driver_tester &parent, int id) : _id(id), _parent(parent) {}
116  virtual ~my_timer_handler() {}
117  DEFINE_CLASS_NAME("my_timer_handler");
118 
119  virtual void handle_timer_callback() {
120  FUNCDEF("handle_timer_callback");
121  if (_parent.in_progress())
122  LOG("saw in progress flag set to true! we interrupted real "
123  "ops, not just sleep!");
124  LOG(a_sprintf("timer%d hit.", _id));
125  timer_test_thread *new_thread = new timer_test_thread(_parent);
126  unique_int id = _parent.threads().add_thread(new_thread, false, NULL_POINTER);
127  // the test thread auto-starts, so we don't let the cabinet start it.
128  if (!id)
129  deadly_error(class_name(), func, "failed to start a new thread.");
130 
131  if (_parent.threads().threads() > MAX_THREADS) {
132  int gone_index = _parent.randomizer().inclusive(0, _parent.threads().threads() - 1);
133  unique_int gone_thread = _parent.threads().thread_ids()[gone_index];
134  _parent.threads().cancel_thread(gone_thread);
135  time_control::sleep_ms(100); // allow thread to start up.
136  }
137  _parent.threads().clean_debris(); // toss any dead threads.
138 
139  LOG(a_sprintf("%d threads checking time_stamp.", _parent.threads().threads()));
140  }
141 
142 private:
143  int _id;
144  timer_driver_tester &_parent;
145 };
146 
148 
149 #define CREATE_TIMER(name, id, dur) \
150  my_timer_handler name(*this, id); \
151  program_wide_timer().set_timer(dur, &name); \
152  LOG(astring("created timer ") + #name + " which hits every " + #dur + " ms")
153 
154 #define ZAP_TIMER(name) \
155  program_wide_timer().zap_timer(&name)
156 
157 int timer_driver_tester::execute()
158 {
159  int next_id = 1001; // we start issuing timer IDs at 1001 for this test.
160 
161  int runtime_ms = DEFAULT_TEST_DURATION;
162  if (application::_global_argc >= 2) {
163  astring passed_runtime = application::_global_argv[1];
164  runtime_ms = passed_runtime.convert(DEFAULT_TEST_DURATION);
165  }
166 
167  // some odd timer cycles below to avoid waiting longer than our short default.
168  CREATE_TIMER(timer1, unique_int(next_id++).raw_id(), 1 * SECOND_ms);
169  CREATE_TIMER(timer2, unique_int(next_id++).raw_id(), 250);
170  CREATE_TIMER(timer3, unique_int(next_id++).raw_id(), 3 * SECOND_ms);
171  CREATE_TIMER(timer4, unique_int(next_id++).raw_id(), 140);
172  CREATE_TIMER(timer5, unique_int(next_id++).raw_id(), 500);
173 
174  LOG("pausing for a while...");
175  time_stamp when_done(runtime_ms);
176  while (time_stamp() < when_done) {
177  _in_progress = true;
178  // do some various calculations in here and see if we're interrupted
179  // during them. it's one thing to be interrupted in the middle of a
180  // sleep, but it's much different to be interrupted in mid operation.
181  int scrob = 1;
182  for (int i = 1; i < 50; i++) {
183  scrob *= i;
184  }
185  _in_progress = false;
186 #ifdef __UNIX__
187  time_control::sleep_ms(100);
188 #else
189  bool okay = event_extensions::poll();
190  if (!okay) break;
191 #endif
192  }
193 
194  ZAP_TIMER(timer1);
195  ZAP_TIMER(timer2);
196  ZAP_TIMER(timer3);
197  ZAP_TIMER(timer4);
198  ZAP_TIMER(timer5);
199 
200  critical_events::alert_message(astring(class_name()) + ": works for those functions tested.");
201 
202  return 0;
203 }
204 
206 
207 HOOPLE_MAIN(timer_driver_tester, )
208 
The application_shell is a base object for console programs.
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
int convert(int default_value) const
Converts the string into a corresponding integer.
Definition: astring.cpp:760
Provides a platform-independent object for adding threads to a program.
Definition: ethread.h:36
Manages a collection of threads.
A unique identifier based on integers.
Definition: unique_id.h:97
Represents a point in time relative to the operating system startup time.
Definition: time_stamp.h:38
timeable is the base for objects that can be hooked into timer events.
Definition: timer_driver.h:33
#define deadly_error(c, f, i)
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
const int SECOND_ms
Number of milliseconds in a second.
Definition: definitions.h:120
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
Useful support functions for unit testing, especially within hoople.
Definition: unit_base.cpp:35
#define randomizer()
#define CREATE_TIMER(name, id, dur)
const int MAX_THREADS
const int DEFAULT_TEST_DURATION
#define LOG(s)
#define ZAP_TIMER(name)