2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* Copyright (C) 1994 Cazamar Systems, Inc. */
12 #include <afs/param.h>
21 /* Locking hierarchy for these critical sections:
23 * 1. lock osi_sleepFDCS
24 * 2. lock osi_critSec[i]
25 * 3. lock osi_sleepInfoAllocCS
28 /* file descriptor for iterating over sleeping threads */
29 osi_fdOps_t osi_sleepFDOps = {
36 * Thread-local storage for sleep Info structures
40 /* critical section serializing contents of all sleep FDs, so that
41 * concurrent GetInfo calls don't damage each other if applied
44 CRITICAL_SECTION osi_sleepFDCS;
46 /* critical regions used for SleepSched to guarantee atomicity.
47 * protects all sleep info structures while they're in the
50 static CRITICAL_SECTION osi_critSec[OSI_SLEEPHASHSIZE];
52 /* the sleep info structure hash table.
53 * all active entries are in here. In addition, deleted entries
54 * may be present, referenced by file descriptors from remote
55 * debuggers; these will have OSI_SLEEPINFO_DELETED set and
58 static osi_sleepInfo_t *osi_sleepers[OSI_SLEEPHASHSIZE];
59 static osi_sleepInfo_t *osi_sleepersEnd[OSI_SLEEPHASHSIZE];
61 /* allocate space for lock operations */
62 osi_lockOps_t *osi_lockOps[OSI_NLOCKTYPES];
64 /* some global statistics */
65 long osi_totalSleeps = 0;
67 /* critical section protecting sleepInfoFreeListp and all sleep entries in
70 CRITICAL_SECTION osi_sleepInfoAllocCS;
72 /* sleep entry free list */
73 osi_sleepInfo_t *osi_sleepInfoFreeListp;
76 unsigned long osi_bootTime;
78 /* count of free entries in free list, protected by osi_sleepInfoAllocCS */
79 long osi_sleepInfoCount=0;
81 /* count of # of allocates of sleep info structures */
82 long osi_sleepInfoAllocs = 0;
84 /* the sleep bucket lock must be held.
85 * Releases the reference count and frees the structure if the item has
88 void osi_ReleaseSleepInfo(osi_sleepInfo_t *ap)
90 if (--ap->refCount == 0 && (ap->states & OSI_SLEEPINFO_DELETED))
91 osi_FreeSleepInfo(ap);
94 /* must be called with sleep bucket locked.
95 * Frees the structure if it has a 0 reference count (and removes it
96 * from the hash bucket). Otherwise, we simply mark the item
97 * for deleting when the ref count hits zero.
99 void osi_FreeSleepInfo(osi_sleepInfo_t *ap)
103 if (ap->refCount > 0) {
104 TlsSetValue(osi_SleepSlot, NULL); /* don't reuse me */
105 ap->states |= OSI_SLEEPINFO_DELETED;
109 /* remove from hash if still there */
110 if (ap->states & OSI_SLEEPINFO_INHASH) {
111 ap->states &= ~OSI_SLEEPINFO_INHASH;
112 idx = osi_SLEEPHASH(ap->value);
113 osi_QRemoveHT((osi_queue_t **) &osi_sleepers[idx], (osi_queue_t **) &osi_sleepersEnd[idx], &ap->q);
116 if (ap->states & OSI_SLEEPINFO_DELETED) {
117 EnterCriticalSection(&osi_sleepInfoAllocCS);
118 ap->q.nextp = (osi_queue_t *) osi_sleepInfoFreeListp;
119 osi_sleepInfoFreeListp = ap;
120 osi_sleepInfoCount++;
121 LeaveCriticalSection(&osi_sleepInfoAllocCS);
125 /* allocate a new sleep structure from the free list */
126 osi_sleepInfo_t *osi_AllocSleepInfo()
130 EnterCriticalSection(&osi_sleepInfoAllocCS);
131 if (!(ap = osi_sleepInfoFreeListp)) {
132 ap = (osi_sleepInfo_t *) malloc(sizeof(osi_sleepInfo_t));
133 ap->sema = CreateSemaphore(NULL, 0, 65536, (char *) 0);
134 osi_sleepInfoAllocs++;
137 osi_sleepInfoFreeListp = (osi_sleepInfo_t *) ap->q.nextp;
138 osi_sleepInfoCount--;
140 ap->tid = GetCurrentThreadId();
141 ap->states = 0; /* not signalled yet */
142 LeaveCriticalSection(&osi_sleepInfoAllocCS);
147 int osi_Once(osi_once_t *argp)
151 while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) {
155 if (argp->done == 0) {
160 /* otherwise we've already been initialized, so clear lock and return */
161 InterlockedExchange(&argp->atomic, 0);
165 void osi_EndOnce(osi_once_t *argp)
167 InterlockedExchange(&argp->atomic, 0);
170 int osi_TestOnce(osi_once_t *argp)
175 while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) {
179 localDone = argp->done;
182 InterlockedExchange(&argp->atomic, 0);
184 return (localDone? 0 : 1);
187 /* Initialize the package, should be called while single-threaded.
188 * Can be safely called multiple times.
189 * Must be called before any osi package calls.
194 static osi_once_t once;
195 unsigned long remainder; /* for division output */
199 osi_hyper_t bootTime;
201 /* check to see if already initialized; if so, claim success */
202 if (!osi_Once(&once)) return;
204 /* setup boot time values */
205 GetSystemTime(&sysTime);
206 SystemTimeToFileTime(&sysTime, &fileTime);
208 /* change the base of the time so it won't be negative for a long time */
209 fileTime.dwHighDateTime -= 28000000;
211 bootTime.HighPart = fileTime.dwHighDateTime;
212 bootTime.LowPart = fileTime.dwLowDateTime;
213 /* now, bootTime is in 100 nanosecond units, and we'd really rather
214 * have it in 1 second units, units 10,000,000 times bigger.
217 bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder);
218 osi_bootTime = bootTime.LowPart;
220 /* initialize thread-local storage for sleep Info structures */
221 osi_SleepSlot = TlsAlloc();
226 /* initialize critical regions and semaphores */
227 for(i=0;i<OSI_SLEEPHASHSIZE; i++) {
228 InitializeCriticalSection(&osi_critSec[i]);
229 osi_sleepers[i] = (osi_sleepInfo_t *) NULL;
230 osi_sleepersEnd[i] = (osi_sleepInfo_t *) NULL;
234 InitializeCriticalSection(&osi_sleepInfoAllocCS);
236 /* initialize cookie system */
237 InitializeCriticalSection(&osi_sleepFDCS);
239 /* register the FD type */
240 typep = osi_RegisterFDType("sleep", &osi_sleepFDOps, NULL);
242 /* add formatting info */
243 osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 0,
244 "Sleep address", OSI_DBRPC_HEX);
245 osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 1,
247 osi_AddFDFormatInfo(typep, OSI_DBRPC_REGIONINT, 2,
248 "States", OSI_DBRPC_HEX);
260 void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, CRITICAL_SECTION *releasep)
265 sp = TlsGetValue(osi_SleepSlot);
267 sp = osi_AllocSleepInfo();
268 TlsSetValue(osi_SleepSlot, sp);
274 sp->waitFor = waitFor;
275 sp->value = (LONG_PTR) patchp;
276 osi_QAddT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
279 LeaveCriticalSection(releasep);
281 /* now wait for the signal */
284 code = WaitForSingleObject(sp->sema,
285 /* timeout */ INFINITE);
287 /* if the reason for the wakeup was that we were signalled,
288 * break out, otherwise try again, since the semaphore count is
289 * decreased only when we get WAIT_OBJECT_0 back.
291 if (code == WAIT_OBJECT_0) break;
292 } /* while we're waiting */
294 /* we're the only one who should be looking at or changing this
295 * structure after it gets signalled. Sema sp->sema isn't signalled
296 * any longer after we're back from WaitForSingleObject, so we can
297 * free this element directly.
299 osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED);
301 osi_FreeSleepInfo(sp);
303 /* reobtain, since caller commonly needs it */
304 EnterCriticalSection(releasep);
307 /* must be called with a critical section held that guards the turnstile
308 * structure. We remove the sleepInfo structure from the queue so we don't
309 * wake the guy again, but we don't free it because we're still using the
310 * semaphore until the guy waiting wakes up.
312 void osi_TSignal(osi_turnstile_t *turnp)
320 turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q);
321 osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
322 sp->states |= OSI_SLEEPINFO_SIGNALLED;
323 ReleaseSemaphore(sp->sema, 1, (long *) 0);
326 /* like TSignal, only wake *everyone* */
327 void osi_TBroadcast(osi_turnstile_t *turnp)
331 while(sp = turnp->lastp) {
332 turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q);
333 osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
334 sp->states |= OSI_SLEEPINFO_SIGNALLED;
335 ReleaseSemaphore(sp->sema, 1, (long *) 0);
336 } /* while someone's still asleep */
339 /* special turnstile signal for mutexes and locks. Wakes up only those who
340 * will really be able to lock the lock. The assumption is that everyone who
341 * already can use the lock has already been woken (and is thus not in the
342 * turnstile any longer).
344 * The stillHaveReaders parm is set to 1 if this is a convert from write to read,
345 * indicating that there is still at least one reader, and we should only wake
346 * up other readers. We use it in a tricky manner: we just pretent we already woke
347 * a reader, and that is sufficient to prevent us from waking a writer.
349 * The crit sec. csp is released before the threads are woken, but after they
350 * are removed from the turnstile. It helps ensure that we won't have a spurious
351 * context swap back to us if the release performs a context swap for some reason.
353 void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SECTION *csp)
355 osi_sleepInfo_t *tsp; /* a temp */
356 osi_sleepInfo_t *nsp; /* a temp */
357 osi_queue_t *wakeupListp; /* list of dudes to wakeup after dropping lock */
362 wokeReader = stillHaveReaders;
364 while(tsp = turnp->lastp) {
365 /* look at each sleepInfo until we find someone we're not supposed to
368 if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
369 if (wokeReader) break;
373 /* otherwise, we will wake this guy. For now, remove from this list
374 * and move to private one, so we can do the wakeup after releasing
377 turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&tsp->q);
378 osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &tsp->q);
380 /* do the patching required for lock obtaining */
381 if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
382 cp = (void *) tsp->value;
383 (*cp) |= OSI_LOCKFLAG_EXCL;
385 else if (tsp->waitFor & OSI_SLEEPINFO_W4READ) {
386 sp = (void *) tsp->value;
390 /* and add to our own list */
391 tsp->q.nextp = wakeupListp;
392 wakeupListp = &tsp->q;
394 /* now if we woke a writer, we're done, since it is pointless
395 * to wake more than one writer.
397 if (!wokeReader) break;
400 /* hit end, or found someone we're not supposed to wakeup */
401 if (csp) LeaveCriticalSection(csp);
403 /* finally, wakeup everyone we found. Don't free things since the sleeper
404 * will free the sleepInfo structure.
406 for(tsp = (osi_sleepInfo_t *) wakeupListp; tsp; tsp = nsp) {
407 /* pull this out first, since *tsp *could* get freed immediately
408 * after the ReleaseSemaphore, if a context swap occurs.
410 nsp = (osi_sleepInfo_t *) tsp->q.nextp;
411 tsp->states |= OSI_SLEEPINFO_SIGNALLED;
412 ReleaseSemaphore(tsp->sema, 1, (long *) 0);
416 /* utility function to atomically (with respect to WakeSched)
417 * release an atomic counter spin lock and sleep on an
419 * Called with no locks held.
421 void osi_SleepSpin(LONG_PTR sleepValue, CRITICAL_SECTION *releasep)
423 register LONG_PTR idx;
426 CRITICAL_SECTION *csp;
428 sp = TlsGetValue(osi_SleepSlot);
430 sp = osi_AllocSleepInfo();
431 TlsSetValue(osi_SleepSlot, sp);
437 sp->value = sleepValue;
438 idx = osi_SLEEPHASH(sleepValue);
439 csp = &osi_critSec[idx];
440 EnterCriticalSection(csp);
441 osi_QAddT((osi_queue_t **) &osi_sleepers[idx], (osi_queue_t **) &osi_sleepersEnd[idx], &sp->q);
442 sp->states |= OSI_SLEEPINFO_INHASH;
443 LeaveCriticalSection(releasep);
444 LeaveCriticalSection(csp);
445 osi_totalSleeps++; /* stats */
448 code = WaitForSingleObject(sp->sema,
449 /* timeout */ INFINITE);
451 /* if the reason for the wakeup was that we were signalled,
452 * break out, otherwise try again, since the semaphore count is
453 * decreased only when we get WAIT_OBJECT_0 back.
455 if (code == WAIT_OBJECT_0) break;
459 EnterCriticalSection(csp);
461 /* must be signalled */
462 osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED);
464 /* free the sleep structure, must be done under bucket lock
465 * so that we can check reference count and serialize with
466 * those who change it.
468 osi_FreeSleepInfo(sp);
470 LeaveCriticalSection(csp);
473 /* utility function to wakeup someone sleeping in SleepSched */
474 void osi_WakeupSpin(LONG_PTR sleepValue)
476 register LONG_PTR idx;
477 register CRITICAL_SECTION *csp;
478 register osi_sleepInfo_t *tsp;
480 idx = osi_SLEEPHASH(sleepValue);
481 csp = &osi_critSec[idx];
482 EnterCriticalSection(csp);
483 for(tsp=osi_sleepers[idx]; tsp; tsp=(osi_sleepInfo_t *) osi_QNext(&tsp->q)) {
484 if ((!(tsp->states & (OSI_SLEEPINFO_DELETED|OSI_SLEEPINFO_SIGNALLED)))
485 && tsp->value == sleepValue) {
486 ReleaseSemaphore(tsp->sema, 1, (long *) 0);
487 tsp->states |= OSI_SLEEPINFO_SIGNALLED;
490 LeaveCriticalSection(csp);
493 void osi_Sleep(LONG_PTR sleepVal)
495 CRITICAL_SECTION *csp;
497 /* may as well save some code by using SleepSched again */
498 csp = &osi_baseAtomicCS[0];
499 EnterCriticalSection(csp);
500 osi_SleepSpin(sleepVal, csp);
503 void osi_Wakeup(LONG_PTR sleepVal)
505 /* how do we do osi_Wakeup on a per-lock package type? */
507 osi_WakeupSpin(sleepVal);
510 long osi_SleepFDCreate(osi_fdType_t *fdTypep, osi_fd_t **outpp)
514 cp = (osi_sleepFD_t *)malloc(sizeof(*cp));
515 memset((void *) cp, 0, sizeof(*cp));
524 long osi_SleepFDClose(osi_fd_t *cp)
530 /* called with osi_sleepFDCS locked; returns with same, so that
531 * we know that the sleep info pointed to by the cookie won't change
532 * until the caller releases the lock.
534 void osi_AdvanceSleepFD(osi_sleepFD_t *cp)
536 int idx; /* index we're dealing with */
537 int oidx; /* index we locked */
538 osi_sleepInfo_t *sip;
539 osi_sleepInfo_t *nsip;
541 idx = 0; /* so we go around once safely */
543 while(idx < OSI_SLEEPHASHSIZE) {
544 /* cp->sip should be held */
546 EnterCriticalSection(&osi_critSec[idx]);
547 oidx = idx; /* remember original index; that's the one we locked */
549 /* if there's a sleep info structure in the FD, it should be held; it
550 * is the one we just processed, so we want to move on to the next.
551 * If not, then we want to process the chain in the bucket idx points
554 if ((sip = cp->sip) == NULL) {
555 sip = osi_sleepers[idx];
557 else sip->refCount++;
560 /* it is safe to release the current sleep info guy now
561 * since we hold the bucket lock. Pull next guy out first,
562 * since if sip is deleted, Release will move him into
565 nsip = (osi_sleepInfo_t *) sip->q.nextp;
566 osi_ReleaseSleepInfo(sip);
569 if (sip) sip->refCount++;
574 LeaveCriticalSection(&osi_critSec[oidx]);
576 /* now, if we advanced to a new sleep info structure, we're
577 * done, otherwise we continue and look at the next hash bucket
578 * until we're out of them.
585 long osi_SleepFDGetInfo(osi_fd_t *ifdp, osi_remGetInfoParms_t *parmsp)
587 osi_sleepFD_t *fdp = (osi_sleepFD_t *) ifdp;
588 osi_sleepInfo_t *sip;
591 /* now, grab a mutex serializing all iterations over FDs, so that
592 * if the RPC screws up and sends us two calls on the same FD, we don't
593 * crash and burn advancing the same FD concurrently. Probably paranoia,
594 * but you generally shouldn't trust stuff coming over the network.
596 EnterCriticalSection(&osi_sleepFDCS);
598 /* this next call advances the FD to the next guy, and simultaneously validates
599 * that the info from the network is valid. If it isn't, we do our best to
600 * resynchronize our position, but we might return some info multiple times.
602 osi_AdvanceSleepFD(fdp);
604 /* now copy out info */
605 if (sip = fdp->sip) { /* one '=' */
606 parmsp->idata[0] = sip->value;
607 parmsp->idata[1] = sip->tid;
608 parmsp->idata[2] = sip->states;
613 else code = OSI_DBRPC_EOF;
615 LeaveCriticalSection(&osi_sleepFDCS);
620 /* finally, DLL-specific code for NT */
621 BOOL APIENTRY DLLMain(HANDLE inst, DWORD why, char *reserved)
626 /* some misc functions for setting hash table sizes */
628 /* return true iff x is prime */
629 int osi_IsPrime(unsigned long x)
633 /* even numbers aren't prime */
634 if ((x & 1) == 0 && x != 2) return 0;
636 for(c = 3; c<x; c += 2) {
637 /* see if x is divisible by c */
638 if ((x % c) == 0) return 0; /* yup, it ain't prime */
640 /* see if we've gone far enough; only have to compute until
643 if (c*c > x) return 1;
646 /* probably never get here */
650 /* return first prime number less than or equal to x */
651 unsigned long osi_PrimeLessThan(unsigned long x) {
654 for(c = x; c > 1; c--) {
655 if (osi_IsPrime(c)) return c;
662 /* return the # of seconds since some fixed date */
663 unsigned long osi_GetBootTime(void)
668 static int (*notifFunc)(char *, char *, long) = NULL;
670 void osi_InitPanic(void *anotifFunc)
672 notifFunc = anotifFunc;
675 void osi_panic(char *msgp, char *filep, long line)
677 osi_LogPanic(filep, line);
680 (*notifFunc)(msgp, filep, line);
683 /* get time in seconds since some relatively recent time */
684 time_t osi_Time(void)
688 unsigned long remainder;
689 LARGE_INTEGER bootTime;
691 /* setup boot time values */
692 GetSystemTime(&sysTime);
693 SystemTimeToFileTime(&sysTime, &fileTime);
695 /* change the base of the time so it won't be negative for a long time */
696 fileTime.dwHighDateTime -= 28000000;
698 bootTime.HighPart = fileTime.dwHighDateTime;
699 bootTime.LowPart = fileTime.dwLowDateTime;
700 /* now, bootTime is in 100 nanosecond units, and we'd really rather
701 * have it in 1 second units, units 10,000,000 times bigger.
704 bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder);
706 return bootTime.QuadPart;
708 return bootTime.LowPart;
712 /* get time in seconds since some relatively recent time */
713 void osi_GetTime(long *timesp)
717 unsigned long remainder;
718 LARGE_INTEGER bootTime;
720 /* setup boot time values */
721 GetSystemTime(&sysTime);
722 SystemTimeToFileTime(&sysTime, &fileTime);
724 /* change the base of the time so it won't be negative for a long time */
725 fileTime.dwHighDateTime -= 28000000;
727 bootTime.HighPart = fileTime.dwHighDateTime;
728 bootTime.LowPart = fileTime.dwLowDateTime;
729 /* now, bootTime is in 100 nanosecond units, and we'd really rather
730 * have it in 1 microsecond units, units 10 times bigger.
733 bootTime = ExtendedLargeIntegerDivide(bootTime, 10, &remainder);
734 bootTime = ExtendedLargeIntegerDivide(bootTime, 1000000, &remainder);
735 timesp[0] = bootTime.LowPart; /* seconds */
736 timesp[1] = remainder; /* microseconds */