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