Windows; GetSystemTimeAsFileTime
[openafs.git] / src / WINNT / client_osi / osisleep.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* Copyright (C) 1994 Cazamar Systems, Inc. */
11
12 #include <afs/param.h>
13 #include <afs/stds.h>
14
15 #include <windows.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <assert.h>
19 #include "osi.h"
20
21 #include "osi_internal.h"
22
23 /* Locking hierarchy for these critical sections:
24  *
25  * 1. lock osi_sleepFDCS
26  * 2. lock osi_critSec[i]
27  * 3. lock osi_sleepInfoAllocCS
28  */
29
30 /* file descriptor for iterating over sleeping threads */
31 osi_fdOps_t osi_sleepFDOps = {
32         osi_SleepFDCreate,
33         osi_SleepFDGetInfo,
34         osi_SleepFDClose
35 };
36
37 /*
38  * Thread-local storage for sleep Info structures
39  */
40 DWORD osi_SleepSlot;
41
42 /* critical section serializing contents of all sleep FDs, so that
43  * concurrent GetInfo calls don't damage each other if applied
44  * to the same FD.
45  */
46 CRITICAL_SECTION osi_sleepFDCS;
47
48 /* critical regions used for SleepSched to guarantee atomicity.
49  * protects all sleep info structures while they're in the
50  * sleep hash table.
51  */
52 static CRITICAL_SECTION osi_critSec[OSI_SLEEPHASHSIZE];
53
54 /* the sleep info structure hash table.
55  * all active entries are in here.  In addition, deleted entries
56  * may be present, referenced by file descriptors from remote
57  * debuggers; these will have OSI_SLEEPINFO_DELETED set and
58  * should be ignored.
59  */
60 static osi_sleepInfo_t *osi_sleepers[OSI_SLEEPHASHSIZE];
61 static osi_sleepInfo_t *osi_sleepersEnd[OSI_SLEEPHASHSIZE];
62
63 /* allocate space for lock operations */
64 osi_lockOps_t *osi_lockOps[OSI_NLOCKTYPES];
65
66 /* some global statistics */
67 long osi_totalSleeps = 0;
68
69 /* critical section protecting sleepInfoFreeListp and all sleep entries in
70  * the free list.
71  */
72 CRITICAL_SECTION osi_sleepInfoAllocCS;
73
74 /* sleep entry free list */
75 osi_sleepInfo_t *osi_sleepInfoFreeListp;
76
77 /* boot time */
78 unsigned long osi_bootTime;
79
80 /* count of free entries in free list, protected by osi_sleepInfoAllocCS */
81 long osi_sleepInfoCount=0;
82
83 /* count of # of allocates of sleep info structures */
84 long osi_sleepInfoAllocs = 0;
85
86 /* the sleep bucket lock must be held.
87  * Releases the reference count and frees the structure if the item has
88  * been deleted.
89  */
90 void osi_ReleaseSleepInfo(osi_sleepInfo_t *sp)
91 {
92     if (InterlockedDecrement(&sp->refCount) == 0 && (sp->states & OSI_SLEEPINFO_DELETED))
93         osi_FreeSleepInfo(sp);
94 }
95
96 /* must be called with sleep bucket locked.
97  * Frees the structure if it has a 0 reference count (and removes it
98  * from the hash bucket).  Otherwise, we simply mark the item
99  * for deleting when the ref count hits zero.
100  */
101 void osi_FreeSleepInfo(osi_sleepInfo_t *sp)
102 {
103     LONG_PTR idx;
104
105     if (sp->refCount > 0) {
106         TlsSetValue(osi_SleepSlot, NULL);       /* don't reuse me */
107         _InterlockedOr(&sp->states, OSI_SLEEPINFO_DELETED);
108         return;
109     }
110
111     /* remove from hash if still there */
112     if (sp->states & OSI_SLEEPINFO_INHASH) {
113         idx = osi_SLEEPHASH(sp->value);
114         osi_QRemoveHT((osi_queue_t **) &osi_sleepers[idx], (osi_queue_t **) &osi_sleepersEnd[idx], &sp->q);
115         _InterlockedAnd(&sp->states, ~OSI_SLEEPINFO_INHASH);
116     }
117
118     if (sp->states & OSI_SLEEPINFO_DELETED) {
119         EnterCriticalSection(&osi_sleepInfoAllocCS);
120         sp->q.nextp = (osi_queue_t *) osi_sleepInfoFreeListp;
121         osi_sleepInfoFreeListp = sp;
122         _InterlockedAnd(&sp->states, ~OSI_SLEEPINFO_DELETED);
123         InterlockedIncrement(&osi_sleepInfoCount);
124         LeaveCriticalSection(&osi_sleepInfoAllocCS);
125     }
126 }
127
128 /* allocate a new sleep structure from the free list */
129 osi_sleepInfo_t *osi_AllocSleepInfo()
130 {
131     osi_sleepInfo_t *sp;
132
133     EnterCriticalSection(&osi_sleepInfoAllocCS);
134     if (!(sp = osi_sleepInfoFreeListp)) {
135         sp = (osi_sleepInfo_t *) malloc(sizeof(osi_sleepInfo_t));
136         memset(sp, 0, sizeof(*sp));
137         sp->sema = CreateSemaphore(NULL, 0, 65536, NULL);
138         InterlockedIncrement(&osi_sleepInfoAllocs);
139     }
140     else {
141         osi_sleepInfoFreeListp = (osi_sleepInfo_t *) sp->q.nextp;
142         InterlockedDecrement(&osi_sleepInfoCount);
143     }
144     sp->tid = GetCurrentThreadId();
145     LeaveCriticalSection(&osi_sleepInfoAllocCS);
146
147     return sp;
148 }
149
150 int osi_Once(osi_once_t *argp)
151 {
152     long i;
153
154     while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) {
155         Sleep(0);
156     }
157
158     if (argp->done == 0) {
159         argp->done = 1;
160         return 1;
161     }
162
163     /* otherwise we've already been initialized, so clear lock and return */
164     InterlockedExchange(&argp->atomic, 0);
165     return 0;
166 }
167
168 void osi_EndOnce(osi_once_t *argp)
169 {
170     InterlockedExchange(&argp->atomic, 0);
171 }
172
173 int osi_TestOnce(osi_once_t *argp)
174 {
175     long localDone;
176     long i;
177
178     while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) {
179         Sleep(0);
180     }
181
182     localDone = argp->done;
183
184     /* drop interlock */
185     InterlockedExchange(&argp->atomic, 0);
186
187     return (localDone? 0 : 1);
188 }
189
190 /* Initialize the package, should be called while single-threaded.
191  * Can be safely called multiple times.
192  * Must be called before any osi package calls.
193  */
194 void osi_Init(void)
195 {
196     int i;
197     static osi_once_t once;
198     unsigned long remainder;            /* for division output */
199     osi_fdType_t *typep;
200     FILETIME fileTime;
201     osi_hyper_t bootTime;
202
203     /* check to see if already initialized; if so, claim success */
204     if (!osi_Once(&once))
205         return;
206
207     /* setup boot time values */
208     GetSystemTimeAsFileTime(&fileTime);
209
210     /* change the base of the time so it won't be negative for a long time */
211     fileTime.dwHighDateTime -= 28000000;
212
213     bootTime.HighPart = fileTime.dwHighDateTime;
214     bootTime.LowPart = fileTime.dwLowDateTime;
215     /* now, bootTime is in 100 nanosecond units, and we'd really rather
216      * have it in 1 second units, units 10,000,000 times bigger.
217      * So, we divide.
218      */
219     bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder);
220     osi_bootTime = bootTime.LowPart;
221
222     /* initialize thread-local storage for sleep Info structures */
223     osi_SleepSlot = TlsAlloc();
224
225     /* init FD system */
226     osi_InitFD();
227
228     /* initialize critical regions and semaphores */
229     for(i=0;i<OSI_SLEEPHASHSIZE; i++) {
230         InitializeCriticalSection(&osi_critSec[i]);
231         osi_sleepers[i] = NULL;
232         osi_sleepersEnd[i] = NULL;
233     }
234
235     /* free list CS */
236     InitializeCriticalSection(&osi_sleepInfoAllocCS);
237
238     /* initialize cookie system */
239     InitializeCriticalSection(&osi_sleepFDCS);
240
241     /* register the FD type */
242     typep = osi_RegisterFDType("sleep", &osi_sleepFDOps, NULL);
243     if (typep) {
244         /* add formatting info */
245         osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 0,
246                              "Sleep address", OSI_DBRPC_HEX);
247         osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 1,
248                              "Thread ID", 0);
249         osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 2,
250                              "States", OSI_DBRPC_HEX);
251     }
252
253     osi_BaseInit();
254
255     osi_StatInit();
256
257     osi_InitQueue();
258
259     osi_EndOnce(&once);
260 }
261
262 void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, DWORD *tidp, CRITICAL_SECTION *releasep)
263 {
264     osi_TWaitExt(turnp, waitFor, patchp, tidp, releasep, TRUE);
265 }
266
267 void osi_TWaitExt(osi_turnstile_t *turnp, int waitFor, void *patchp, DWORD *tidp, CRITICAL_SECTION *releasep, int prepend)
268 {
269     osi_sleepInfo_t *sp;
270     unsigned int code;
271
272     sp = TlsGetValue(osi_SleepSlot);
273     if (sp == NULL) {
274         sp = osi_AllocSleepInfo();
275         TlsSetValue(osi_SleepSlot, sp);
276     }
277     else {
278         _InterlockedAnd(&sp->states, 0);
279     }
280     sp->waitFor = waitFor;
281     sp->value = (LONG_PTR) patchp;
282     sp->tidp = tidp;
283     sp->idx = -1;
284     if (prepend)
285         osi_QAddH((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
286     else
287         osi_QAddT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
288     LeaveCriticalSection(releasep);
289
290     /* now wait for the signal */
291     while(1) {
292         /* wait */
293         code = WaitForSingleObject(sp->sema,
294                                     /* timeout */ INFINITE);
295
296         /* if the reason for the wakeup was that we were signalled,
297          * break out, otherwise try again, since the semaphore count is
298          * decreased only when we get WAIT_OBJECT_0 back.
299          */
300         if (code == WAIT_OBJECT_0) break;
301     }   /* while we're waiting */
302
303     /* we're the only one who should be looking at or changing this
304      * structure after it gets signalled.  Sema sp->sema isn't signalled
305      * any longer after we're back from WaitForSingleObject, so we can
306      * free this element directly.
307      */
308     osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED);
309
310     osi_FreeSleepInfo(sp);
311
312     /* reobtain, since caller commonly needs it */
313     EnterCriticalSection(releasep);
314 }
315
316 /* must be called with a critical section held that guards the turnstile
317  * structure.  We remove the sleepInfo structure from the queue so we don't
318  * wake the guy again, but we don't free it because we're still using the
319  * semaphore until the guy waiting wakes up.
320  */
321 void osi_TSignal(osi_turnstile_t *turnp)
322 {
323     osi_sleepInfo_t *sp;
324
325     if (!turnp->lastp)
326         return;
327
328     sp = turnp->lastp;
329     osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
330     _InterlockedOr(&sp->states, OSI_SLEEPINFO_SIGNALLED);
331     ReleaseSemaphore(sp->sema, 1, NULL);
332 }
333
334 /* like TSignal, only wake *everyone* */
335 void osi_TBroadcast(osi_turnstile_t *turnp)
336 {
337     osi_sleepInfo_t *sp;
338
339     while(sp = turnp->lastp) {
340         osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
341         _InterlockedOr(&sp->states, OSI_SLEEPINFO_SIGNALLED);
342         ReleaseSemaphore(sp->sema, 1, NULL);
343     }   /* while someone's still asleep */
344 }
345
346 /* special turnstile signal for mutexes and locks.  Wakes up only those who
347  * will really be able to lock the lock.  The assumption is that everyone who
348  * already can use the lock has already been woken (and is thus not in the
349  * turnstile any longer).
350  *
351  * The stillHaveReaders parm is set to 1 if this is a convert from write to read,
352  * indicating that there is still at least one reader, and we should only wake
353  * up other readers.  We use it in a tricky manner: we just pretent we already woke
354  * a reader, and that is sufficient to prevent us from waking a writer.
355  *
356  * The crit sec. csp is released before the threads are woken, but after they
357  * are removed from the turnstile.  It helps ensure that we won't have a spurious
358  * context swap back to us if the release performs a context swap for some reason.
359  */
360 void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SECTION *csp)
361 {
362     osi_sleepInfo_t *tsp;               /* a temp */
363     osi_sleepInfo_t *nsp;               /* a temp */
364     osi_queue_t *wakeupListp;   /* list of dudes to wakeup after dropping lock */
365     int wokeReader;
366     unsigned short *sp;
367     unsigned char *cp;
368
369     wokeReader = stillHaveReaders;
370     wakeupListp = NULL;
371     while(tsp = turnp->lastp) {
372         /* look at each sleepInfo until we find someone we're not supposed to
373          * wakeup.
374          */
375         if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
376             if (wokeReader)
377                 break;
378         }
379         else
380             wokeReader = 1;
381
382         /* otherwise, we will wake this guy.  For now, remove from this list
383          * and move to private one, so we can do the wakeup after releasing
384          * the crit sec.
385          */
386         osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &tsp->q);
387
388         /* do the patching required for lock obtaining */
389         if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
390             cp = (void *) tsp->value;
391             (*cp) |= OSI_LOCKFLAG_EXCL;
392             tsp->tidp[0] = tsp->tid;
393         }
394         else if (tsp->waitFor & OSI_SLEEPINFO_W4READ) {
395             sp = (void *) tsp->value;
396 #ifdef DEBUG
397             if ((*sp) < OSI_RWLOCK_THREADS)
398                 tsp->tidp[*sp] = tsp->tid;
399 #endif
400             (*sp)++;
401         }
402
403         /* and add to our own list */
404         tsp->q.nextp = wakeupListp;
405         wakeupListp = &tsp->q;
406
407         /* now if we woke a writer, we're done, since it is pointless
408          * to wake more than one writer.
409          */
410         if (!wokeReader)
411             break;
412     }
413
414     /* hit end, or found someone we're not supposed to wakeup */
415     if (csp)
416         LeaveCriticalSection(csp);
417
418         /* finally, wakeup everyone we found.  Don't free things since the sleeper
419          * will free the sleepInfo structure.
420          */
421         for(tsp = (osi_sleepInfo_t *) wakeupListp; tsp; tsp = nsp) {
422             /* pull this out first, since *tsp *could* get freed immediately
423              * after the ReleaseSemaphore, if a context swap occurs.
424              */
425             nsp = (osi_sleepInfo_t *) tsp->q.nextp;
426             _InterlockedOr(&tsp->states, OSI_SLEEPINFO_SIGNALLED);
427             ReleaseSemaphore(tsp->sema, 1, NULL);
428         }
429 }
430
431 /* utility function to atomically (with respect to WakeSched)
432  * release an atomic counter spin lock and sleep on an
433  * address (value).
434  * Called with no locks held.
435  */
436 void osi_SleepSpin(LONG_PTR sleepValue, CRITICAL_SECTION *releasep)
437 {
438     int code;
439     osi_sleepInfo_t *sp;
440     CRITICAL_SECTION *csp;
441
442     sp = TlsGetValue(osi_SleepSlot);
443     if (sp == NULL) {
444         sp = osi_AllocSleepInfo();
445         TlsSetValue(osi_SleepSlot, sp);
446     }
447     else {
448         _InterlockedAnd(&sp->states, 0);
449     }
450     sp->waitFor = 0;
451     sp->value = sleepValue;
452     sp->tidp = NULL;
453     sp->idx = osi_SLEEPHASH(sleepValue);
454     csp = &osi_critSec[sp->idx];
455     EnterCriticalSection(csp);
456     osi_QAddT((osi_queue_t **) &osi_sleepers[sp->idx], (osi_queue_t **) &osi_sleepersEnd[sp->idx], &sp->q);
457     _InterlockedOr(&sp->states, OSI_SLEEPINFO_INHASH);
458     LeaveCriticalSection(csp);
459     LeaveCriticalSection(releasep);
460     InterlockedIncrement(&osi_totalSleeps);     /* stats */
461     while(1) {
462         /* wait */
463         code = WaitForSingleObject(sp->sema,
464                                     /* timeout */ INFINITE);
465
466         /* if the reason for the wakeup was that we were signalled,
467         * break out, otherwise try again, since the semaphore count is
468         * decreased only when we get WAIT_OBJECT_0 back.
469         */
470         if (code == WAIT_OBJECT_0) break;
471     }
472
473     /* now clean up */
474     EnterCriticalSection(csp);
475
476     /* must be signalled */
477     osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED);
478
479     /* free the sleep structure, must be done under bucket lock
480      * so that we can check reference count and serialize with
481      * those who change it.
482      */
483     osi_FreeSleepInfo(sp);
484
485     LeaveCriticalSection(csp);
486 }
487
488 /* utility function to wakeup someone sleeping in SleepSched */
489 void osi_WakeupSpin(LONG_PTR sleepValue)
490 {
491     LONG_PTR idx;
492     CRITICAL_SECTION *csp;
493     osi_sleepInfo_t *tsp;
494
495     idx = osi_SLEEPHASH(sleepValue);
496     csp = &osi_critSec[idx];
497     EnterCriticalSection(csp);
498     for(tsp=osi_sleepers[idx]; tsp; tsp=(osi_sleepInfo_t *) osi_QNext(&tsp->q)) {
499         if ((!(tsp->states & (OSI_SLEEPINFO_DELETED|OSI_SLEEPINFO_SIGNALLED)))
500              && tsp->value == sleepValue) {
501             _InterlockedOr(&tsp->states, OSI_SLEEPINFO_SIGNALLED);
502             ReleaseSemaphore(tsp->sema, 1, NULL);
503         }
504     }
505     LeaveCriticalSection(csp);
506 }
507
508 void osi_Sleep(LONG_PTR sleepVal)
509 {
510     CRITICAL_SECTION *csp;
511
512     /* may as well save some code by using SleepSched again */
513     csp = &osi_baseAtomicCS[0];
514     EnterCriticalSection(csp);
515     osi_SleepSpin(sleepVal, csp);
516 }
517
518 void osi_Wakeup(LONG_PTR sleepVal)
519 {
520     /* how do we do osi_Wakeup on a per-lock package type? */
521
522     osi_WakeupSpin(sleepVal);
523 }
524
525 long osi_SleepFDCreate(osi_fdType_t *fdTypep, osi_fd_t **outpp)
526 {
527     osi_sleepFD_t *cp;
528
529     cp = (osi_sleepFD_t *)malloc(sizeof(*cp));
530     memset((void *) cp, 0, sizeof(*cp));
531     cp->idx = 0;
532     cp->sip = NULL;
533
534     /* done */
535     *outpp = &cp->fd;
536     return 0;
537 }
538
539 long osi_SleepFDClose(osi_fd_t *cp)
540 {
541     free((void *) cp);
542     return 0;
543 }
544
545 /* called with osi_sleepFDCS locked; returns with same, so that
546  * we know that the sleep info pointed to by the cookie won't change
547  * until the caller releases the lock.
548  */
549 void osi_AdvanceSleepFD(osi_sleepFD_t *cp)
550 {
551     int idx;            /* index we're dealing with */
552     int oidx;           /* index we locked */
553     osi_sleepInfo_t *sip;
554     osi_sleepInfo_t *nsip;
555
556     idx = 0;    /* so we go around once safely */
557     sip = NULL;
558     while(idx < OSI_SLEEPHASHSIZE) {
559         /* cp->sip should be held */
560         idx = cp->idx;
561         EnterCriticalSection(&osi_critSec[idx]);
562         oidx = idx;     /* remember original index; that's the one we locked */
563
564         /* if there's a sleep info structure in the FD, it should be held; it
565          * is the one we just processed, so we want to move on to the next.
566          * If not, then we want to process the chain in the bucket idx points
567          * to.
568          */
569         if ((sip = cp->sip) == NULL) {
570             sip = osi_sleepers[idx];
571             if (!sip) idx++;
572             else
573                 InterlockedIncrement(&sip->refCount);
574         }
575         else {
576             /* it is safe to release the current sleep info guy now
577              * since we hold the bucket lock.  Pull next guy out first,
578              * since if sip is deleted, Release will move him into
579              * free list.
580              */
581             nsip = (osi_sleepInfo_t *) sip->q.nextp;
582             osi_ReleaseSleepInfo(sip);
583             sip = nsip;
584
585             if (sip)
586                 InterlockedIncrement(&sip->refCount);
587             else
588                 idx++;
589         }
590         cp->idx = idx;
591         cp->sip = sip;
592         LeaveCriticalSection(&osi_critSec[oidx]);
593
594         /* now, if we advanced to a new sleep info structure, we're
595          * done, otherwise we continue and look at the next hash bucket
596          * until we're out of them.
597          */
598         if (sip)
599             break;
600     }
601 }
602
603
604 long osi_SleepFDGetInfo(osi_fd_t *ifdp, osi_remGetInfoParms_t *parmsp)
605 {
606     osi_sleepFD_t *fdp = (osi_sleepFD_t *) ifdp;
607     osi_sleepInfo_t *sip;
608     long code;
609
610     /* now, grab a mutex serializing all iterations over FDs, so that
611      * if the RPC screws up and sends us two calls on the same FD, we don't
612      * crash and burn advancing the same FD concurrently.  Probably paranoia,
613      * but you generally shouldn't trust stuff coming over the network.
614      */
615     EnterCriticalSection(&osi_sleepFDCS);
616
617     /* this next call advances the FD to the next guy, and simultaneously validates
618      * that the info from the network is valid.  If it isn't, we do our best to
619      * resynchronize our position, but we might return some info multiple times.
620      */
621     osi_AdvanceSleepFD(fdp);
622
623     /* now copy out info */
624     if (sip = fdp->sip) {       /* one '=' */
625         parmsp->idata[0] = sip->value;
626         parmsp->idata[1] = sip->tid;
627         parmsp->idata[2] = sip->states;
628         parmsp->icount = 3;
629         parmsp->scount = 0;
630         code = 0;
631     }
632     else code = OSI_DBRPC_EOF;
633
634     LeaveCriticalSection(&osi_sleepFDCS);
635
636     return code;
637 }
638
639 /* finally, DLL-specific code for NT */
640 BOOL APIENTRY DLLMain(HANDLE inst, DWORD why, char *reserved)
641 {
642     return 1;
643 }
644
645 /* some misc functions for setting hash table sizes */
646
647 /* return true iff x is prime */
648 int osi_IsPrime(unsigned long x)
649 {
650     unsigned long c;
651
652     /* even numbers aren't prime */
653     if ((x & 1) == 0 && x != 2) return 0;
654
655     for(c = 3; c<x; c += 2) {
656         /* see if x is divisible by c */
657         if ((x % c) == 0)
658             return 0;   /* yup, it ain't prime */
659
660         /* see if we've gone far enough; only have to compute until
661          * square root of x.
662          */
663         if (c*c > x)
664             return 1;
665     }
666
667     /* probably never get here */
668     return 1;
669 }
670
671 /* return first prime number less than or equal to x */
672 unsigned long osi_PrimeLessThan(unsigned long x) {
673     unsigned long c;
674
675     for(c = x; c > 1; c--) {
676         if (osi_IsPrime(c))
677             return c;
678     }
679
680     /* ever reached? */
681     return 1;
682 }
683
684 /* return the # of seconds since some fixed date */
685 unsigned long osi_GetBootTime(void)
686 {
687     return osi_bootTime;
688 }
689
690 static int (*notifFunc)(char *, char *, long) = NULL;
691
692 void osi_InitPanic(void *anotifFunc)
693 {
694     notifFunc = anotifFunc;
695 }
696
697 void osi_panic(char *msgp, char *filep, long line)
698 {
699     if (notifFunc)
700         (*notifFunc)(msgp, filep, line);
701
702     osi_LogPanic(msgp, filep, line);
703 }
704
705 /* get time in seconds since some relatively recent time */
706 time_t osi_Time(void)
707 {
708     FILETIME fileTime;
709     unsigned long remainder;
710     LARGE_INTEGER bootTime;
711
712     /* setup boot time values */
713     GetSystemTimeAsFileTime(&fileTime);
714
715     /* change the base of the time so it won't be negative for a long time */
716     fileTime.dwHighDateTime -= 28000000;
717
718     bootTime.HighPart = fileTime.dwHighDateTime;
719     bootTime.LowPart = fileTime.dwLowDateTime;
720     /* now, bootTime is in 100 nanosecond units, and we'd really rather
721      * have it in 1 second units, units 10,000,000 times bigger.
722      * So, we divide.
723      */
724     bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder);
725 #ifdef __WIN64
726     return bootTime.QuadPart;
727 #else
728     return bootTime.LowPart;
729 #endif
730 }
731
732 /* get time in seconds since some relatively recent time */
733 void osi_GetTime(long *timesp)
734 {
735     FILETIME fileTime;
736     unsigned long remainder;
737     LARGE_INTEGER bootTime;
738
739     /* setup boot time values */
740     GetSystemTimeAsFileTime(&fileTime);
741
742     /* change the base of the time so it won't be negative for a long time */
743     fileTime.dwHighDateTime -= 28000000;
744
745     bootTime.HighPart = fileTime.dwHighDateTime;
746     bootTime.LowPart = fileTime.dwLowDateTime;
747     /* now, bootTime is in 100 nanosecond units, and we'd really rather
748      * have it in 1 microsecond units, units 10 times bigger.
749      * So, we divide.
750      */
751     bootTime = ExtendedLargeIntegerDivide(bootTime, 10, &remainder);
752     bootTime = ExtendedLargeIntegerDivide(bootTime, 1000000, &remainder);
753     timesp[0] = bootTime.LowPart;               /* seconds */
754     timesp[1] = remainder;                      /* microseconds */
755 }