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/. */
5
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 */
32
33 #include "nspr.h"
34 #include "plgetopt.h"
35
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
50
51 #define DEFAULT_LEAD_TIME_SECS 5
52 #define DEFAULT_TOLERANCE_MSECS 500
53
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;
59
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
70
SleepThread(void * arg)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
85
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 }
121
AcceptThread(void * arg)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;
139
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 }
197
PollThread(void * arg)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;
216
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 }
276
WaitCondVarThread(void * arg)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;
293
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 }
340
WaitMonitorThread(void * arg)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;
356
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 }
397
WaitCMonitorThread(void * arg)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;
413
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 }
448
449 typedef void (*NSPRThreadFunc)(void*);
450
451 static NSPRThreadFunc threadFuncs[] = {
452 SleepThread, AcceptThread, PollThread,
453 WaitCondVarThread, WaitMonitorThread, WaitCMonitorThread
454 };
455
456 static PRThreadScope threadScopes[] = {
457 PR_LOCAL_THREAD, PR_GLOBAL_THREAD, PR_GLOBAL_BOUND_THREAD
458 };
459
Help(void)460 static void Help(void)
461 {
462 fprintf(stderr, "y2ktmo test program usage:\n");
463 fprintf(stderr, "\t-d debug mode (FALSE)\n");
464 fprintf(stderr, "\t-l <secs> lead time (%d)\n",
465 DEFAULT_LEAD_TIME_SECS);
466 fprintf(stderr, "\t-t <msecs> tolerance (%d)\n",
467 DEFAULT_TOLERANCE_MSECS);
468 fprintf(stderr, "\t-h this message\n");
469 } /* Help */
470
main(int argc,char ** argv)471 int main(int argc, char **argv)
472 {
473 PRThread **threads;
474 int num_thread_funcs = sizeof(threadFuncs)/sizeof(NSPRThreadFunc);
475 int num_thread_scopes = sizeof(threadScopes)/sizeof(PRThreadScope);
476 int i, j;
477 int idx;
478 PRInt32 secs;
479 PLOptStatus os;
480 PLOptState *opt = PL_CreateOptState(argc, argv, "dl:t:h");
481
482 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
483 if (PL_OPT_BAD == os) {
484 continue;
485 }
486 switch (opt->option) {
487 case 'd': /* debug mode */
488 debug_mode = PR_TRUE;
489 break;
490 case 'l': /* lead time */
491 lead_time_secs = atoi(opt->value);
492 break;
493 case 't': /* tolerance */
494 tolerance_msecs = atoi(opt->value);
495 break;
496 case 'h':
497 default:
498 Help();
499 return 2;
500 }
501 }
502 PL_DestroyOptState(opt);
503
504 if (debug_mode) {
505 fprintf(stderr, "lead time: %d secs\n", lead_time_secs);
506 fprintf(stderr, "tolerance: %d msecs\n", tolerance_msecs);
507 }
508
509 start_time = PR_IntervalNow();
510 #if defined(XP_UNIX)
511 gettimeofday(&start_time_tv, NULL);
512 #endif
513 #if defined(WIN32)
514 #ifdef WINCE
515 start_time_tick = GetTickCount();
516 #else
517 _ftime(&start_time_tb);
518 #endif
519 #endif
520 tolerance = PR_MillisecondsToInterval(tolerance_msecs);
521
522 threads = PR_Malloc(
523 num_thread_scopes * num_thread_funcs * sizeof(PRThread*));
524 if (threads == NULL) {
525 fprintf(stderr, "PR_Malloc failed\n");
526 exit(1);
527 }
528
529 /* start to time out 5 seconds after a rollover date */
530 secs = lead_time_secs + 5;
531 idx = 0;
532 for (i = 0; i < num_thread_scopes; i++) {
533 for (j = 0; j < num_thread_funcs; j++) {
534 threads[idx] = PR_CreateThread(PR_USER_THREAD, threadFuncs[j],
535 (void*)PR_SecondsToInterval(secs), PR_PRIORITY_NORMAL,
536 threadScopes[i], PR_JOINABLE_THREAD, 0);
537 if (threads[idx] == NULL) {
538 fprintf(stderr, "PR_CreateThread failed\n");
539 exit(1);
540 }
541 secs++;
542 idx++;
543 }
544 }
545 for (idx = 0; idx < num_thread_scopes*num_thread_funcs; idx++) {
546 if (PR_JoinThread(threads[idx]) == PR_FAILURE) {
547 fprintf(stderr, "PR_JoinThread failed\n");
548 exit(1);
549 }
550 }
551 PR_Free(threads);
552 printf("PASS\n");
553 return 0;
554 }
555