Commit | Line | Data |
---|---|---|
d44e3c4f | 1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * Balasko, Jeno | |
10 | * Delic, Adam | |
11 | * Kovacs, Ferenc | |
12 | * Raduly, Csaba | |
13 | * Szabo, Janos Zoltan – initial implementation | |
14 | * | |
15 | ******************************************************************************/ | |
970ed795 EL |
16 | #include "Timer.hh" |
17 | #include "Float.hh" | |
18 | #include "Error.hh" | |
19 | #include "Logger.hh" | |
20 | #include "Snapshot.hh" | |
21 | ||
22 | TIMER *TIMER::list_head = NULL, *TIMER::list_tail = NULL, | |
23 | *TIMER::backup_head = NULL, *TIMER::backup_tail = NULL; | |
24 | boolean TIMER::control_timers_saved = FALSE; | |
25 | ||
26 | void TIMER::add_to_list() | |
27 | { | |
28 | // do nothing if already a member of list | |
29 | if (this == list_head || list_prev != NULL) return; | |
30 | if (list_head == NULL) list_head = this; | |
31 | else if (list_tail != NULL) list_tail->list_next = this; | |
32 | list_prev = list_tail; | |
33 | list_next = NULL; | |
34 | list_tail = this; | |
35 | } | |
36 | ||
37 | void TIMER::remove_from_list() | |
38 | { | |
39 | if (list_prev != NULL) list_prev->list_next = list_next; | |
40 | else if (list_head == this) list_head = list_next; | |
41 | if (list_next != NULL) list_next->list_prev = list_prev; | |
42 | else if (list_tail == this) list_tail = list_prev; | |
43 | list_prev = NULL; | |
44 | list_next = NULL; | |
45 | } | |
46 | ||
47 | TIMER::TIMER(const char *par_timer_name) | |
48 | { | |
49 | timer_name = par_timer_name != NULL ? par_timer_name : "<unknown>"; | |
50 | has_default = FALSE; | |
51 | is_started = FALSE; | |
52 | list_prev = NULL; | |
53 | list_next = NULL; | |
54 | } | |
55 | ||
56 | TIMER::TIMER(const char *par_timer_name, double def_val) | |
57 | { | |
58 | if (par_timer_name == NULL) | |
59 | TTCN_error("Internal error: Creating a timer with an invalid name."); | |
60 | timer_name = par_timer_name; | |
61 | set_default_duration(def_val); | |
62 | is_started = FALSE; | |
63 | list_prev = NULL; | |
64 | list_next = NULL; | |
65 | } | |
66 | ||
67 | TIMER::TIMER(const char *par_timer_name, const FLOAT& def_val) | |
68 | { | |
69 | if (par_timer_name == NULL) | |
70 | TTCN_error("Internal error: Creating a timer with an invalid name."); | |
71 | timer_name = par_timer_name; | |
72 | def_val.must_bound("Initializing a timer duration with an unbound " | |
73 | "float value."); | |
74 | set_default_duration(def_val.float_value); | |
75 | is_started = FALSE; | |
76 | list_prev = NULL; | |
77 | list_next = NULL; | |
78 | } | |
79 | ||
80 | TIMER::~TIMER() | |
81 | { | |
82 | if (is_started) remove_from_list(); | |
83 | } | |
84 | ||
85 | void TIMER::set_name(const char * name) | |
86 | { | |
87 | if (name == NULL) TTCN_error("Internal error: Setting an " | |
88 | "invalid name for a single element of a timer array."); | |
89 | timer_name = name; | |
90 | } | |
91 | ||
92 | void TIMER::set_default_duration(double def_val) | |
93 | { | |
94 | if (def_val < 0.0) { | |
95 | TTCN_error("Setting the default duration of timer %s " | |
96 | "to a negative float value (%g).", timer_name, def_val); | |
97 | } else if (FLOAT::is_special(def_val)) { | |
98 | TTCN_error("Setting the default duration of timer %s " | |
99 | "to a non-numeric float value (%g).", timer_name, def_val); | |
100 | } | |
101 | has_default = TRUE; | |
102 | default_val = def_val; | |
103 | } | |
104 | ||
105 | void TIMER::set_default_duration(const FLOAT& def_val) | |
106 | { | |
107 | if (!def_val.is_bound()) TTCN_error("Setting the default duration of " | |
108 | "timer %s to an unbound float value.", timer_name); | |
109 | set_default_duration(def_val.float_value); | |
110 | } | |
111 | ||
112 | void TIMER::start() | |
113 | { | |
114 | if (!has_default) | |
115 | TTCN_error("Timer %s does not have default duration. " | |
116 | "It can only be started with a given duration.", timer_name); | |
117 | start(default_val); | |
118 | } | |
119 | ||
120 | void TIMER::start(double start_val) | |
121 | { | |
122 | if (this != &testcase_timer) { | |
123 | if (start_val < 0.0) | |
124 | TTCN_error("Starting timer %s with a negative duration (%g).", | |
125 | timer_name, start_val); | |
126 | if (is_started) { | |
127 | TTCN_warning("Re-starting timer %s, which is already active (running " | |
128 | "or expired).", timer_name); | |
129 | remove_from_list(); | |
130 | } else { | |
131 | is_started = TRUE; | |
132 | } | |
133 | TTCN_Logger::log_timer_start(timer_name, start_val); | |
134 | add_to_list(); | |
135 | } else { | |
136 | if (start_val < 0.0) | |
137 | TTCN_error("Using a negative duration (%g) for the guard timer of the " | |
138 | "test case.", start_val); | |
139 | is_started = TRUE; | |
140 | TTCN_Logger::log_timer_guard(start_val); | |
141 | } | |
142 | t_started = TTCN_Snapshot::time_now(); | |
143 | t_expires = t_started + start_val; | |
144 | } | |
145 | ||
146 | void TIMER::start(const FLOAT& start_val) | |
147 | { | |
148 | if (!start_val.is_bound()) | |
149 | TTCN_error("Starting timer %s with an unbound float value as duration.", | |
150 | timer_name); | |
151 | start(start_val.float_value); | |
152 | } | |
153 | ||
154 | void TIMER::stop() | |
155 | { | |
156 | if (this != &testcase_timer) { | |
157 | if (is_started) { | |
158 | is_started = FALSE; | |
159 | TTCN_Logger::log_timer_stop(timer_name, t_expires - t_started); | |
160 | remove_from_list(); | |
161 | } else TTCN_warning("Stopping inactive timer %s.", timer_name); | |
162 | } else is_started = FALSE; | |
163 | } | |
164 | ||
165 | double TIMER::read() const | |
166 | { | |
167 | // the time is not frozen (i.e. time_now() is used) | |
168 | double ret_val; | |
169 | if (is_started) { | |
170 | double current_time = TTCN_Snapshot::time_now(); | |
171 | if (current_time >= t_expires) ret_val = 0.0; | |
172 | else ret_val = current_time - t_started; | |
173 | } else { | |
174 | ret_val = 0.0; | |
175 | } | |
176 | TTCN_Logger::log_timer_read(timer_name, ret_val); | |
177 | return ret_val; | |
178 | } | |
179 | ||
180 | boolean TIMER::running() const | |
181 | { | |
182 | // the time is not frozen (i.e. time_now() is used) | |
183 | return is_started && TTCN_Snapshot::time_now() < t_expires; | |
184 | } | |
185 | ||
186 | alt_status TIMER::timeout() | |
187 | { | |
188 | // the time is frozen (i.e. get_alt_begin() is used) | |
189 | if (is_started) { | |
190 | if (TTCN_Snapshot::get_alt_begin() < t_expires) return ALT_MAYBE; | |
191 | is_started = FALSE; | |
192 | if (this != &testcase_timer) { | |
193 | TTCN_Logger::log_timer_timeout(timer_name, t_expires - t_started); | |
194 | remove_from_list(); | |
195 | } | |
196 | return ALT_YES; | |
197 | } else { | |
198 | if (this != &testcase_timer) { | |
199 | TTCN_Logger::log_matching_timeout(timer_name); | |
200 | } | |
201 | return ALT_NO; | |
202 | } | |
203 | } | |
204 | ||
205 | void TIMER::log() const | |
206 | { | |
207 | // the time is not frozen (i.e. time_now() is used) | |
208 | TTCN_Logger::log_event("timer: { name: %s, default duration: ", timer_name); | |
209 | if (has_default) TTCN_Logger::log_event("%g s", default_val); | |
210 | else TTCN_Logger::log_event_str("none"); | |
211 | TTCN_Logger::log_event_str(", state: "); | |
212 | if (is_started) { | |
213 | double current_time = TTCN_Snapshot::time_now(); | |
214 | if (current_time < t_expires) TTCN_Logger::log_event_str("running"); | |
215 | else TTCN_Logger::log_event_str("expired"); | |
216 | TTCN_Logger::log_event(", actual duration: %g s, " | |
217 | "elapsed time: %g s", t_expires - t_started, | |
218 | current_time - t_started); | |
219 | } else TTCN_Logger::log_event_str("inactive"); | |
220 | TTCN_Logger::log_event_str(" }"); | |
221 | } | |
222 | ||
223 | void TIMER::all_stop() | |
224 | { | |
225 | while (list_head != NULL) list_head->stop(); // also removes from list | |
226 | } | |
227 | ||
228 | boolean TIMER::any_running() | |
229 | { | |
230 | for (TIMER *list_iter = list_head; list_iter != NULL; | |
231 | list_iter = list_iter->list_next) { | |
232 | if (list_iter->running()) return TRUE; | |
233 | } | |
234 | return FALSE; | |
235 | } | |
236 | ||
237 | alt_status TIMER::any_timeout() | |
238 | { | |
239 | alt_status ret_val = ALT_NO; | |
240 | for (TIMER *list_iter = list_head; list_iter != NULL; | |
241 | list_iter = list_iter->list_next) { | |
242 | switch (list_iter->timeout()) { | |
243 | case ALT_YES: | |
244 | TTCN_Logger::log_timer_any_timeout(); | |
245 | return ALT_YES; | |
246 | case ALT_MAYBE: | |
247 | ret_val = ALT_MAYBE; | |
248 | break; | |
249 | default: | |
250 | TTCN_error("Internal error: Timer %s returned unexpected status " | |
251 | "code while evaluating `any timer.timeout'.", | |
252 | list_iter->timer_name); | |
253 | } | |
254 | } | |
255 | if (ret_val == ALT_NO) { | |
256 | TTCN_Logger::log_matching_timeout(NULL); | |
257 | } | |
258 | return ret_val; | |
259 | } | |
260 | ||
261 | boolean TIMER::get_min_expiration(double& min_val) | |
262 | { | |
263 | boolean min_flag = FALSE; | |
264 | double alt_begin = TTCN_Snapshot::get_alt_begin(); | |
265 | if (testcase_timer.is_started && testcase_timer.t_expires >= alt_begin) { | |
266 | min_val = testcase_timer.t_expires; | |
267 | min_flag = TRUE; | |
268 | } | |
269 | for (TIMER *list_iter = list_head; list_iter != NULL; | |
270 | list_iter = list_iter->list_next) { | |
271 | // timers that are expired before the previous snapshot are ignored | |
272 | if (list_iter->t_expires < alt_begin) continue; | |
273 | else if (!min_flag || list_iter->t_expires < min_val) { | |
274 | min_val = list_iter->t_expires; | |
275 | min_flag = TRUE; | |
276 | } | |
277 | } | |
278 | return min_flag; | |
279 | } | |
280 | ||
281 | void TIMER::save_control_timers() | |
282 | { | |
283 | if (control_timers_saved) | |
284 | TTCN_error("Internal error: Control part timers are already saved."); | |
285 | // put the list of control part timers into the backup | |
286 | backup_head = list_head; | |
287 | list_head = NULL; | |
288 | backup_tail = list_tail; | |
289 | list_tail = NULL; | |
290 | control_timers_saved = TRUE; | |
291 | } | |
292 | ||
293 | void TIMER::restore_control_timers() | |
294 | { | |
295 | if (!control_timers_saved) | |
296 | TTCN_error("Internal error: Control part timers are not saved."); | |
297 | if (list_head != NULL) | |
298 | TTCN_error("Internal error: There are active timers. " | |
299 | "Control part timers cannot be restored."); | |
300 | // restore the list of control part timers from the backup | |
301 | list_head = backup_head; | |
302 | backup_head = NULL; | |
303 | list_tail = backup_tail; | |
304 | backup_tail = NULL; | |
305 | control_timers_saved = FALSE; | |
306 | } | |
307 | ||
308 | TIMER testcase_timer("<testcase guard timer>"); |