Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * Test: y2ktmo
8 *
9 * Description:
10 * This test tests the interval time facilities in NSPR for Y2K
11 * compliance. All the functions that take a timeout argument
12 * are tested: PR_Sleep, socket I/O (PR_Accept is taken as a
13 * representative), PR_Poll, PR_WaitCondVar, PR_Wait, and
14 * PR_CWait. A thread of each thread scope (local, global, and
15 * global bound) is created to call each of these functions.
16 * The test should be started at the specified number of seconds
17 * (called the lead time) before a Y2K rollover test date. The
18 * timeout values for these threads will span over the rollover
19 * date by at least the specified number of seconds. For
20 * example, if the lead time is 5 seconds, the test should
21 * be started at time (D - 5), where D is a rollover date, and
22 * the threads will time out at or after time (D + 5). The
23 * timeout values for the threads are spaced one second apart.
24 *
25 * When a thread times out, it calls PR_IntervalNow() to verify
26 * that it did wait for the specified time. In addition, it
27 * calls a platform-native function to verify the actual elapsed
28 * time again, to rule out the possibility that PR_IntervalNow()
29 * is broken. We allow the actual elapsed time to deviate from
30 * the specified timeout by a certain tolerance (in milliseconds).
31 */
33 #include "nspr.h"
34 #include "plgetopt.h"
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #if defined(XP_UNIX)
40 #include <sys/time.h> /* for gettimeofday */
41 #endif
42 #if defined(WIN32)
43 #if defined(WINCE)
44 #include <windows.h>
45 #else
46 #include <sys/types.h>
47 #include <sys/timeb.h> /* for _ftime */
48 #endif
49 #endif
51 #define DEFAULT_LEAD_TIME_SECS 5
52 #define DEFAULT_TOLERANCE_MSECS 500
54 static PRBool debug_mode = PR_FALSE;
55 static PRInt32 lead_time_secs = DEFAULT_LEAD_TIME_SECS;
56 static PRInt32 tolerance_msecs = DEFAULT_TOLERANCE_MSECS;
57 static PRIntervalTime start_time;
58 static PRIntervalTime tolerance;
60 #if defined(XP_UNIX)
61 static struct timeval start_time_tv;
62 #endif
63 #if defined(WIN32)
64 #if defined(WINCE)
65 static DWORD start_time_tick;
66 #else
67 static struct _timeb start_time_tb;
68 #endif
69 #endif
71 static void SleepThread(void *arg)
72 {
73 PRIntervalTime timeout = (PRIntervalTime) arg;
74 PRIntervalTime elapsed;
75 #if defined(XP_UNIX) || defined(WIN32)
76 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
77 PRInt32 elapsed_msecs;
78 #endif
79 #if defined(XP_UNIX)
80 struct timeval end_time_tv;
81 #endif
82 #if defined(WIN32) && !defined(WINCE)
83 struct _timeb end_time_tb;
84 #endif
86 if (PR_Sleep(timeout) == PR_FAILURE) {
87 fprintf(stderr, "PR_Sleep failed\n");
88 exit(1);
89 }
90 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
91 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
92 fprintf(stderr, "timeout wrong\n");
93 exit(1);
94 }
95 #if defined(XP_UNIX)
96 gettimeofday(&end_time_tv, NULL);
97 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
98 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
99 #endif
100 #if defined(WIN32)
101 #if defined(WINCE)
102 elapsed_msecs = GetTickCount() - start_time_tick;
103 #else
104 _ftime(&end_time_tb);
105 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
106 + (end_time_tb.millitm - start_time_tb.millitm);
107 #endif
108 #endif
109 #if defined(XP_UNIX) || defined(WIN32)
110 if (elapsed_msecs + tolerance_msecs < timeout_msecs
111 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
112 fprintf(stderr, "timeout wrong\n");
113 exit(1);
114 }
115 #endif
116 if (debug_mode) {
117 fprintf(stderr, "Sleep thread (scope %d) done\n",
118 PR_GetThreadScope(PR_GetCurrentThread()));
119 }
120 }
122 static void AcceptThread(void *arg)
123 {
124 PRIntervalTime timeout = (PRIntervalTime) arg;
125 PRIntervalTime elapsed;
126 #if defined(XP_UNIX) || defined(WIN32)
127 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
128 PRInt32 elapsed_msecs;
129 #endif
130 #if defined(XP_UNIX)
131 struct timeval end_time_tv;
132 #endif
133 #if defined(WIN32) && !defined(WINCE)
134 struct _timeb end_time_tb;
135 #endif
136 PRFileDesc *sock;
137 PRNetAddr addr;
138 PRFileDesc *accepted;
140 sock = PR_NewTCPSocket();
141 if (sock == NULL) {
142 fprintf(stderr, "PR_NewTCPSocket failed\n");
143 exit(1);
144 }
145 memset(&addr, 0, sizeof(addr));
146 addr.inet.family = PR_AF_INET;
147 addr.inet.port = 0;
148 addr.inet.ip = PR_htonl(PR_INADDR_ANY);
149 if (PR_Bind(sock, &addr) == PR_FAILURE) {
150 fprintf(stderr, "PR_Bind failed\n");
151 exit(1);
152 }
153 if (PR_Listen(sock, 5) == PR_FAILURE) {
154 fprintf(stderr, "PR_Listen failed\n");
155 exit(1);
156 }
157 accepted = PR_Accept(sock, NULL, timeout);
158 if (accepted != NULL || PR_GetError() != PR_IO_TIMEOUT_ERROR) {
159 fprintf(stderr, "PR_Accept did not time out\n");
160 exit(1);
161 }
162 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
163 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
164 fprintf(stderr, "timeout wrong\n");
165 exit(1);
166 }
167 #if defined(XP_UNIX)
168 gettimeofday(&end_time_tv, NULL);
169 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
170 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
171 #endif
172 #if defined(WIN32)
173 #if defined(WINCE)
174 elapsed_msecs = GetTickCount() - start_time_tick;
175 #else
176 _ftime(&end_time_tb);
177 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
178 + (end_time_tb.millitm - start_time_tb.millitm);
179 #endif
180 #endif
181 #if defined(XP_UNIX) || defined(WIN32)
182 if (elapsed_msecs + tolerance_msecs < timeout_msecs
183 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
184 fprintf(stderr, "timeout wrong\n");
185 exit(1);
186 }
187 #endif
188 if (PR_Close(sock) == PR_FAILURE) {
189 fprintf(stderr, "PR_Close failed\n");
190 exit(1);
191 }
192 if (debug_mode) {
193 fprintf(stderr, "Accept thread (scope %d) done\n",
194 PR_GetThreadScope(PR_GetCurrentThread()));
195 }
196 }
198 static void PollThread(void *arg)
199 {
200 PRIntervalTime timeout = (PRIntervalTime) arg;
201 PRIntervalTime elapsed;
202 #if defined(XP_UNIX) || defined(WIN32)
203 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
204 PRInt32 elapsed_msecs;
205 #endif
206 #if defined(XP_UNIX)
207 struct timeval end_time_tv;
208 #endif
209 #if defined(WIN32) && !defined(WINCE)
210 struct _timeb end_time_tb;
211 #endif
212 PRFileDesc *sock;
213 PRNetAddr addr;
214 PRPollDesc pd;
215 PRIntn rv;
217 sock = PR_NewTCPSocket();
218 if (sock == NULL) {
219 fprintf(stderr, "PR_NewTCPSocket failed\n");
220 exit(1);
221 }
222 memset(&addr, 0, sizeof(addr));
223 addr.inet.family = PR_AF_INET;
224 addr.inet.port = 0;
225 addr.inet.ip = PR_htonl(PR_INADDR_ANY);
226 if (PR_Bind(sock, &addr) == PR_FAILURE) {
227 fprintf(stderr, "PR_Bind failed\n");
228 exit(1);
229 }
230 if (PR_Listen(sock, 5) == PR_FAILURE) {
231 fprintf(stderr, "PR_Listen failed\n");
232 exit(1);
233 }
234 pd.fd = sock;
235 pd.in_flags = PR_POLL_READ;
236 rv = PR_Poll(&pd, 1, timeout);
237 if (rv != 0) {
238 fprintf(stderr, "PR_Poll did not time out\n");
239 exit(1);
240 }
241 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
242 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
243 fprintf(stderr, "timeout wrong\n");
244 exit(1);
245 }
246 #if defined(XP_UNIX)
247 gettimeofday(&end_time_tv, NULL);
248 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
249 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
250 #endif
251 #if defined(WIN32)
252 #if defined(WINCE)
253 elapsed_msecs = GetTickCount() - start_time_tick;
254 #else
255 _ftime(&end_time_tb);
256 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
257 + (end_time_tb.millitm - start_time_tb.millitm);
258 #endif
259 #endif
260 #if defined(XP_UNIX) || defined(WIN32)
261 if (elapsed_msecs + tolerance_msecs < timeout_msecs
262 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
263 fprintf(stderr, "timeout wrong\n");
264 exit(1);
265 }
266 #endif
267 if (PR_Close(sock) == PR_FAILURE) {
268 fprintf(stderr, "PR_Close failed\n");
269 exit(1);
270 }
271 if (debug_mode) {
272 fprintf(stderr, "Poll thread (scope %d) done\n",
273 PR_GetThreadScope(PR_GetCurrentThread()));
274 }
275 }
277 static void WaitCondVarThread(void *arg)
278 {
279 PRIntervalTime timeout = (PRIntervalTime) arg;
280 PRIntervalTime elapsed;
281 #if defined(XP_UNIX) || defined(WIN32)
282 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
283 PRInt32 elapsed_msecs;
284 #endif
285 #if defined(XP_UNIX)
286 struct timeval end_time_tv;
287 #endif
288 #if defined(WIN32) && !defined(WINCE)
289 struct _timeb end_time_tb;
290 #endif
291 PRLock *ml;
292 PRCondVar *cv;
294 ml = PR_NewLock();
295 if (ml == NULL) {
296 fprintf(stderr, "PR_NewLock failed\n");
297 exit(1);
298 }
299 cv = PR_NewCondVar(ml);
300 if (cv == NULL) {
301 fprintf(stderr, "PR_NewCondVar failed\n");
302 exit(1);
303 }
304 PR_Lock(ml);
305 PR_WaitCondVar(cv, timeout);
306 PR_Unlock(ml);
307 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
308 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
309 fprintf(stderr, "timeout wrong\n");
310 exit(1);
311 }
312 #if defined(XP_UNIX)
313 gettimeofday(&end_time_tv, NULL);
314 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
315 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
316 #endif
317 #if defined(WIN32)
318 #if defined(WINCE)
319 elapsed_msecs = GetTickCount() - start_time_tick;
320 #else
321 _ftime(&end_time_tb);
322 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
323 + (end_time_tb.millitm - start_time_tb.millitm);
324 #endif
325 #endif
326 #if defined(XP_UNIX) || defined(WIN32)
327 if (elapsed_msecs + tolerance_msecs < timeout_msecs
328 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
329 fprintf(stderr, "timeout wrong\n");
330 exit(1);
331 }
332 #endif
333 PR_DestroyCondVar(cv);
334 PR_DestroyLock(ml);
335 if (debug_mode) {
336 fprintf(stderr, "wait cond var thread (scope %d) done\n",
337 PR_GetThreadScope(PR_GetCurrentThread()));
338 }
339 }
341 static void WaitMonitorThread(void *arg)
342 {
343 PRIntervalTime timeout = (PRIntervalTime) arg;
344 PRIntervalTime elapsed;
345 #if defined(XP_UNIX) || defined(WIN32)
346 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
347 PRInt32 elapsed_msecs;
348 #endif
349 #if defined(XP_UNIX)
350 struct timeval end_time_tv;
351 #endif
352 #if defined(WIN32) && !defined(WINCE)
353 struct _timeb end_time_tb;
354 #endif
355 PRMonitor *mon;
357 mon = PR_NewMonitor();
358 if (mon == NULL) {
359 fprintf(stderr, "PR_NewMonitor failed\n");
360 exit(1);
361 }
362 PR_EnterMonitor(mon);
363 PR_Wait(mon, timeout);
364 PR_ExitMonitor(mon);
365 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
366 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
367 fprintf(stderr, "timeout wrong\n");
368 exit(1);
369 }
370 #if defined(XP_UNIX)
371 gettimeofday(&end_time_tv, NULL);
372 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
373 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
374 #endif
375 #if defined(WIN32)
376 #if defined(WINCE)
377 elapsed_msecs = GetTickCount() - start_time_tick;
378 #else
379 _ftime(&end_time_tb);
380 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
381 + (end_time_tb.millitm - start_time_tb.millitm);
382 #endif
383 #endif
384 #if defined(XP_UNIX) || defined(WIN32)
385 if (elapsed_msecs + tolerance_msecs < timeout_msecs
386 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
387 fprintf(stderr, "timeout wrong\n");
388 exit(1);
389 }
390 #endif
391 PR_DestroyMonitor(mon);
392 if (debug_mode) {
393 fprintf(stderr, "wait monitor thread (scope %d) done\n",
394 PR_GetThreadScope(PR_GetCurrentThread()));
395 }
396 }
398 static void WaitCMonitorThread(void *arg)
399 {
400 PRIntervalTime timeout = (PRIntervalTime) arg;
401 PRIntervalTime elapsed;
402 #if defined(XP_UNIX) || defined(WIN32)
403 PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
404 PRInt32 elapsed_msecs;
405 #endif
406 #if defined(XP_UNIX)
407 struct timeval end_time_tv;
408 #endif
409 #if defined(WIN32) && !defined(WINCE)
410 struct _timeb end_time_tb;
411 #endif
412 int dummy;
414 PR_CEnterMonitor(&dummy);
415 PR_CWait(&dummy, timeout);
416 PR_CExitMonitor(&dummy);
417 elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
418 if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
419 fprintf(stderr, "timeout wrong\n");
420 exit(1);
421 }
422 #if defined(XP_UNIX)
423 gettimeofday(&end_time_tv, NULL);
424 elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
425 + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
426 #endif
427 #if defined(WIN32)
428 #if defined(WINCE)
429 elapsed_msecs = GetTickCount() - start_time_tick;
430 #else
431 _ftime(&end_time_tb);
432 elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
433 + (end_time_tb.millitm - start_time_tb.millitm);
434 #endif
435 #endif
436 #if defined(XP_UNIX) || defined(WIN32)
437 if (elapsed_msecs + tolerance_msecs < timeout_msecs
438 || elapsed_msecs > timeout_msecs + tolerance_msecs) {
439 fprintf(stderr, "timeout wrong\n");
440 exit(1);
441 }
442 #endif
443 if (debug_mode) {
444 fprintf(stderr, "wait cached monitor thread (scope %d) done\n",
445 PR_GetThreadScope(PR_GetCurrentThread()));
446 }
447 }
449 typedef void (*NSPRThreadFunc)(void*);
451 static NSPRThreadFunc threadFuncs[] = {
452 SleepThread, AcceptThread, PollThread,
453 WaitCondVarThread, WaitMonitorThread, WaitCMonitorThread};
455 static PRThreadScope threadScopes[] = {
456 PR_LOCAL_THREAD, PR_GLOBAL_THREAD, PR_GLOBAL_BOUND_THREAD};
458 static void Help(void)
459 {
460 fprintf(stderr, "y2ktmo test program usage:\n");
461 fprintf(stderr, "\t-d debug mode (FALSE)\n");
462 fprintf(stderr, "\t-l <secs> lead time (%d)\n",
463 DEFAULT_LEAD_TIME_SECS);
464 fprintf(stderr, "\t-t <msecs> tolerance (%d)\n",
465 DEFAULT_TOLERANCE_MSECS);
466 fprintf(stderr, "\t-h this message\n");
467 } /* Help */
469 int main(int argc, char **argv)
470 {
471 PRThread **threads;
472 int num_thread_funcs = sizeof(threadFuncs)/sizeof(NSPRThreadFunc);
473 int num_thread_scopes = sizeof(threadScopes)/sizeof(PRThreadScope);
474 int i, j;
475 int idx;
476 PRInt32 secs;
477 PLOptStatus os;
478 PLOptState *opt = PL_CreateOptState(argc, argv, "dl:t:h");
480 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
481 if (PL_OPT_BAD == os) continue;
482 switch (opt->option) {
483 case 'd': /* debug mode */
484 debug_mode = PR_TRUE;
485 break;
486 case 'l': /* lead time */
487 lead_time_secs = atoi(opt->value);
488 break;
489 case 't': /* tolerance */
490 tolerance_msecs = atoi(opt->value);
491 break;
492 case 'h':
493 default:
494 Help();
495 return 2;
496 }
497 }
498 PL_DestroyOptState(opt);
500 if (debug_mode) {
501 fprintf(stderr, "lead time: %d secs\n", lead_time_secs);
502 fprintf(stderr, "tolerance: %d msecs\n", tolerance_msecs);
503 }
505 start_time = PR_IntervalNow();
506 #if defined(XP_UNIX)
507 gettimeofday(&start_time_tv, NULL);
508 #endif
509 #if defined(WIN32)
510 #ifdef WINCE
511 start_time_tick = GetTickCount();
512 #else
513 _ftime(&start_time_tb);
514 #endif
515 #endif
516 tolerance = PR_MillisecondsToInterval(tolerance_msecs);
518 threads = PR_Malloc(
519 num_thread_scopes * num_thread_funcs * sizeof(PRThread*));
520 if (threads == NULL) {
521 fprintf(stderr, "PR_Malloc failed\n");
522 exit(1);
523 }
525 /* start to time out 5 seconds after a rollover date */
526 secs = lead_time_secs + 5;
527 idx = 0;
528 for (i = 0; i < num_thread_scopes; i++) {
529 for (j = 0; j < num_thread_funcs; j++) {
530 threads[idx] = PR_CreateThread(PR_USER_THREAD, threadFuncs[j],
531 (void*)PR_SecondsToInterval(secs), PR_PRIORITY_NORMAL,
532 threadScopes[i], PR_JOINABLE_THREAD, 0);
533 if (threads[idx] == NULL) {
534 fprintf(stderr, "PR_CreateThread failed\n");
535 exit(1);
536 }
537 secs++;
538 idx++;
539 }
540 }
541 for (idx = 0; idx < num_thread_scopes*num_thread_funcs; idx++) {
542 if (PR_JoinThread(threads[idx]) == PR_FAILURE) {
543 fprintf(stderr, "PR_JoinThread failed\n");
544 exit(1);
545 }
546 }
547 PR_Free(threads);
548 printf("PASS\n");
549 return 0;
550 }