Imported Upstream version 3.2.2
[debian/gnuradio] / mblock / src / lib / qa_timeouts.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <qa_timeouts.h>
26 #include <cppunit/TestAssert.h>
27 #include <mblock/mblock.h>
28 #include <mblock/runtime.h>
29 #include <mblock/protocol_class.h>
30 #include <mblock/message.h>
31 #include <mblock/msg_accepter.h>
32 #include <mblock/class_registry.h>
33 #include <mb_timer_queue.h>
34 #include <string.h>
35 #include <iostream>
36
37
38 static pmt_t s_timeout = pmt_intern("%timeout");
39 static pmt_t s_done = pmt_intern("done");
40
41
42 // ------------------------------------------------------------------------
43 //    Exercise the priority queue used to implement timeouts.
44 // ------------------------------------------------------------------------
45 void
46 qa_timeouts::test_timer_queue()
47 {
48   mb_timer_queue        tq;
49   mb_msg_accepter_sptr  accepter;
50
51   mb_timeout_sptr       t1000_000 =
52     mb_timeout_sptr(new mb_timeout(mb_time(1000,0), PMT_F, accepter));
53
54   mb_timeout_sptr       t2000_000 =
55     mb_timeout_sptr(new mb_timeout(mb_time(2000,0), PMT_F, accepter));
56                                                                     
57   mb_timeout_sptr       t3000_000 =
58     mb_timeout_sptr(new mb_timeout(mb_time(3000,0), PMT_F, accepter));
59                                                                     
60   mb_timeout_sptr       t3000_125 =
61     mb_timeout_sptr(new mb_timeout(mb_time(3000,125), PMT_F, accepter));
62                                                                     
63   mb_timeout_sptr       t3000_250 =
64     mb_timeout_sptr(new mb_timeout(mb_time(3000,250), PMT_F, accepter));
65                                                                     
66   mb_timeout_sptr       t4000_000 =
67     mb_timeout_sptr(new mb_timeout(mb_time(4000,0), PMT_F, accepter));
68                                                                     
69   // insert in pseudo-random order
70
71   tq.push(t3000_125);
72   tq.push(t1000_000);
73   tq.push(t4000_000);
74   tq.push(t3000_250);
75   tq.push(t2000_000);
76   tq.push(t3000_000);
77
78   CPPUNIT_ASSERT_EQUAL(t1000_000, tq.top());
79   tq.pop();
80   
81   CPPUNIT_ASSERT_EQUAL(t2000_000, tq.top());
82   tq.pop();
83   
84   CPPUNIT_ASSERT_EQUAL(t3000_000, tq.top());
85   tq.pop();
86   
87   CPPUNIT_ASSERT_EQUAL(t3000_125, tq.top());
88   tq.pop();
89   
90   CPPUNIT_ASSERT_EQUAL(t3000_250, tq.top());
91   tq.pop();
92   
93   CPPUNIT_ASSERT_EQUAL(t4000_000, tq.top());
94   tq.pop();
95
96   CPPUNIT_ASSERT(tq.empty());
97
98   // insert in pseudo-random order
99
100   tq.push(t3000_000);
101   tq.push(t4000_000);
102   tq.push(t3000_125);
103   tq.push(t1000_000);
104   tq.push(t2000_000);
105   tq.push(t3000_250);
106
107   tq.cancel(t1000_000->handle());
108
109   CPPUNIT_ASSERT_EQUAL(t2000_000, tq.top());
110   tq.pop();
111   
112   CPPUNIT_ASSERT_EQUAL(t3000_000, tq.top());
113   tq.pop();
114   
115   tq.cancel(t3000_250->handle());
116
117   CPPUNIT_ASSERT_EQUAL(t3000_125, tq.top());
118   tq.pop();
119   
120   CPPUNIT_ASSERT_EQUAL(t4000_000, tq.top());
121   tq.pop();
122   
123   CPPUNIT_ASSERT(tq.empty());
124 }
125
126 // ------------------------------------------------------------------------
127 //   Test one-shot timeouts
128 // ------------------------------------------------------------------------
129
130 // FWIW, on SuSE 10.1 for x86-64, clock_getres returns 0.004 seconds.
131
132 // #define TIMING_MARGIN 0.010  // seconds   // was failing on some systems
133 #define TIMING_MARGIN 0.025     // seconds  (really sloppy; consider enabling RT scheduler)
134
135
136 class qa_timeouts_1_top : public mb_mblock
137 {
138   int           d_nleft;
139   int           d_nerrors;
140   mb_time       d_t0;
141   
142 public:
143   qa_timeouts_1_top(mb_runtime *runtime,
144                     const std::string &instance_name, pmt_t user_arg);
145
146   void initial_transition();
147   void handle_message(mb_message_sptr msg);
148 };
149
150 qa_timeouts_1_top::qa_timeouts_1_top(mb_runtime *runtime,
151                                      const std::string &instance_name,
152                                      pmt_t user_arg)
153   : mb_mblock(runtime, instance_name, user_arg),
154     d_nleft(0), d_nerrors(0)
155 {
156 }
157
158 void
159 qa_timeouts_1_top::initial_transition()
160 {
161   d_t0 = mb_time::time();       // now
162
163   schedule_one_shot_timeout(d_t0 + 0.200, pmt_from_double(0.200));
164   schedule_one_shot_timeout(d_t0 + 0.125, pmt_from_double(0.125));
165   schedule_one_shot_timeout(d_t0 + 0.075, pmt_from_double(0.075));
166   schedule_one_shot_timeout(d_t0 + 0.175, pmt_from_double(0.175));
167
168   d_nleft = 4;
169 }
170
171 void
172 qa_timeouts_1_top::handle_message(mb_message_sptr msg)
173 {
174   if (pmt_eq(msg->signal(), s_timeout)){
175     mb_time t_now = mb_time::time();
176     double expected_delta_t = pmt_to_double(msg->data());
177     double actual_delta_t = (t_now - d_t0).double_time();
178     double delta = expected_delta_t - actual_delta_t;
179
180     if (fabs(delta) > TIMING_MARGIN){
181       std::cerr << "qa_timeouts_1_top: expected_delta_t = " << expected_delta_t
182                 << " actual_delta_t = " << actual_delta_t << std::endl;
183       d_nerrors++;
184     }
185
186     if (--d_nleft <= 0)
187       shutdown_all(d_nerrors == 0 ? PMT_T : PMT_F);
188   }
189 }
190
191 REGISTER_MBLOCK_CLASS(qa_timeouts_1_top);
192
193 void
194 qa_timeouts::test_timeouts_1()
195 {
196   mb_runtime_sptr rt = mb_make_runtime();
197   pmt_t result = PMT_NIL;
198
199   rt->run("top", "qa_timeouts_1_top", PMT_F, &result);
200
201   CPPUNIT_ASSERT(pmt_equal(PMT_T, result));
202 }
203
204 // ------------------------------------------------------------------------
205 //   Test periodic timeouts
206 // ------------------------------------------------------------------------
207
208 class qa_timeouts_2_top : public mb_mblock
209 {
210   int           d_nhandled;
211   int           d_nerrors;
212   double        d_delta_t;
213   mb_time       d_t0;
214   
215 public:
216   qa_timeouts_2_top(mb_runtime *runtime,
217                     const std::string &instance_name, pmt_t user_arg);
218
219   void initial_transition();
220   void handle_message(mb_message_sptr msg);
221 };
222
223 qa_timeouts_2_top::qa_timeouts_2_top(mb_runtime *runtime,
224                                      const std::string &instance_name,
225                                      pmt_t user_arg)
226   : mb_mblock(runtime, instance_name, user_arg),
227     d_nhandled(0), d_nerrors(0), d_delta_t(0.075)
228 {
229 }
230
231 void
232 qa_timeouts_2_top::initial_transition()
233 {
234   d_t0 = mb_time::time();       // now
235
236   schedule_periodic_timeout(d_t0 + d_delta_t, mb_time(d_delta_t), PMT_T);
237 }
238
239 void
240 qa_timeouts_2_top::handle_message(mb_message_sptr msg)
241 {
242   static const int NMSGS_TO_HANDLE = 5;
243
244   if (pmt_eq(msg->signal(), s_timeout)
245       && !pmt_eq(msg->data(), s_done)){
246
247     mb_time t_now = mb_time::time();
248
249     d_nhandled++;
250
251     double expected_delta_t = d_delta_t * d_nhandled;
252     double actual_delta_t = (t_now - d_t0).double_time();
253     double delta = expected_delta_t - actual_delta_t;
254
255     if (fabs(delta) > TIMING_MARGIN){
256       std::cerr << "qa_timeouts_2_top: expected_delta_t = " << expected_delta_t
257                 << " actual_delta_t = " << actual_delta_t << std::endl;
258       d_nerrors++;
259     }
260
261     if (d_nhandled == NMSGS_TO_HANDLE){
262       cancel_timeout(msg->metadata());  // test cancel_timeout...
263       schedule_one_shot_timeout(d_t0 + (d_delta_t * (d_nhandled + 2)), s_done);
264     }
265   }
266
267   if (pmt_eq(msg->signal(), s_timeout)
268       && pmt_eq(msg->data(), s_done)){
269     if (d_nhandled != NMSGS_TO_HANDLE){
270       std::cerr << "qa_timeouts_2_top: d_nhandled = " << d_nhandled
271                 << " expected d_nhandled = " << NMSGS_TO_HANDLE
272                 << " (cancel_timeout didn't work)\n";
273       d_nerrors++;
274     }
275     shutdown_all(d_nerrors == 0 ? PMT_T : PMT_F);
276   }
277 }
278
279 REGISTER_MBLOCK_CLASS(qa_timeouts_2_top);
280
281 void
282 qa_timeouts::test_timeouts_2()
283 {
284   mb_runtime_sptr rt = mb_make_runtime();
285   pmt_t result = PMT_NIL;
286
287   rt->run("top", "qa_timeouts_2_top", PMT_F, &result);
288
289   CPPUNIT_ASSERT(pmt_equal(PMT_T, result));
290 }