/* Add or remove an asynchronous tickee. If periodms is non zero add the * tickee, calling it every periodms. * * N.B. addHighPriorityTickee is called from the VM thread, whereas * checkHighPriorityTickees is called from the high-priority heartbeat thread * (or an interrupt). The above 64-bit variables must therefore be read and * written atomically to avoid either thread reading or writing a modified * half of the variable while the other half has yet to be updated. */ void addHighPriorityTickee(void (*tickee)(void), unsigned periodms) { int i; if (!periodms) { for (i = 0; i < numAsyncTickees; i++) /* We cannot safely copy the data to keep used tickees contiguous * because checkHighPriorityTickees could be called during the move. * This implies first checking for an existing tickee below before * using an empty slot because an empty slot can be created before * a used (and subsequently modified) tickee. */ if (async[i].tickee == tickee) { async[i].tickee = 0; sqLowLevelMFence(); return; } return; } for (i = 0; i < numAsyncTickees; i++) if (async[i].tickee == tickee) break; if (i >= numAsyncTickees) for (i = 0; i < NUM_ASYNCHRONOUS_TICKEES; i++) if (i >= numAsyncTickees || !async[i].tickee) break; if (i >= NUM_ASYNCHRONOUS_TICKEES) error("ran out of asyncronous tickee slots"); /* first disable the tickee while updating the entry. */ async[i].tickee = 0; sqLowLevelMFence(); async[i].tickeePeriodUsecs = periodms * MicrosecondsPerMillisecond; async[i].tickeeDeadlineUsecs = async[i].tickeePeriodUsecs + ioUTCMicroseconds(); async[i].inProgress = 0; async[i].tickee = tickee; if (i >= numAsyncTickees) ++numAsyncTickees; sqLowLevelMFence(); }
static inline void lockSignalQueue() { volatile int old; /* spin to obtain a lock */ do { sqLowLevelMFence(); sqCompareAndSwapRes(sigLock, 0, 1, old ); } while (old != 0); }
void checkHighPriorityTickees(usqLong utcMicrosecondClock) { int i; #if ITIMER_HEARTBEAT extern void unblockVMThreadAfterYieldToHighPriorityTickerThread(void); shouldYieldToHighPriorityTickerThread = 1; #endif /* Since this runs either in a high-priority thread or in an interrupt only * one fence is needed. Since the VM thread will not disturb any non-zero * entry (except for changing the period) we can read the entry without * locking. But we need to lock the attempt to run the tickee in case for * any reason checkHighPriorityTickees is miscalled reentrantly. */ sqLowLevelMFence(); for (i = 0; i < numAsyncTickees; i++) if (async[i].tickee && !async[i].inProgress && utcMicrosecondClock >= async[i].tickeeDeadlineUsecs) { int previousInProgress; sqCompareAndSwapRes(async[i].inProgress,0,1,previousInProgress); if (previousInProgress == 0) { assert(async[i].inProgress); if (async[i].tickeeDeadlineUsecs + HiccupThreshold < utcMicrosecondClock) async[i].tickeeDeadlineUsecs = utcMicrosecondClock + async[i].tickeePeriodUsecs; else async[i].tickeeDeadlineUsecs += async[i].tickeePeriodUsecs; async[i].tickee(); async[i].inProgress = 0; } } #if ITIMER_HEARTBEAT shouldYieldToHighPriorityTickerThread = 0; sqLowLevelMFence(); unblockVMThreadAfterYieldToHighPriorityTickerThread(); #endif }
void hptick(void) { unsigned long long start = ioUTCMicroseconds(); yield = 1; sqLowLevelMFence(); while (ioUTCMicroseconds() - start < hptickusecs) if ((hpcount += 1ULL) % CHECK_COUNT == 0 && ioHeartbeatFrequency(0) == 0) lockedup("in hptick"); yield = 0; sqLowLevelMFence(); if (yieldMethod == yield_via_wait_signal) { if (pthread_mutex_trylock(&yield_sync) == 0) {/* success */ pthread_mutex_unlock(&yield_sync); not_blocked_count += 1; } else { pthread_cond_signal(&yield_cond); unblock_count += 1; } } }
void ioSynchronousCheckForEvents() { int i; #if ITIMER_HEARTBEAT extern void yieldToHighPriorityTickerThread(void); sqLowLevelMFence(); if (shouldYieldToHighPriorityTickerThread) yieldToHighPriorityTickerThread(); #endif for (i = 0; i < numSyncTickees; i++) if (synch[i].tickee && ioUTCMicroseconds() >= synch[i].tickeeDeadlineUsecs) { synch[i].tickeeDeadlineUsecs += synch[i].tickeePeriodUsecs; synch[i].tickee(); } }
/* Signal the external semaphore with the given index. Answer non-zero on * success, zero otherwise. This function is (should be) thread-safe; * multiple threads may attempt to signal the same semaphore without error. * An index of zero should be and is silently ignored. * * (sig) As well as negative index. */ sqInt signalSemaphoreWithIndex(sqInt index) { int next; /* An index of zero should be and is silently ignored. */ if (index <=0) return 0; /* we must use the locking semantics to avoid ABA problem on writing a semaphore index to queue, so there is no chance for fetching thread to observe queue in inconsistent state. */ lockSignalQueue(); /* check for queue overflow */ next = succIndex(sigIndexHigh); if (next == sigIndexLow ) { #if SQ_DYNAMIC_QUEUE_SIZE // grow and retry unlockSignalQueue(); ioGrowSignalQueue( maxPendingSignals + 100); return signalSemaphoreWithIndex(index); #else unlockSignalQueue(); // error if queue size is static (perhaps better would be to sleep for a while and retry?) error("External semaphore signal queue overflow"); #endif } signalQueue[sigIndexHigh] = index; /* make sure semaphore index is written before we advance sigIndexHigh */ sqLowLevelMFence(); sigIndexHigh = next; /* reset lock */ unlockSignalQueue(); forceInterruptCheck(); return 1; }
void maybeYield() { sqLowLevelMFence(); if (!yield) return; yield_count += 1; switch (yieldMethod) { case no_yield: break; case yield_via_sched_yield: sched_yield(); break; case yield_via_pthread_yield: pthread_yield(); break; case yield_via_nanosleep: { struct timespec yieldtime; yieldtime.tv_sec = 0; yieldtime.tv_nsec = 10 * 1000; break; } case yield_via_cond_timedwait: { struct timespec yieldtime; yieldtime.tv_sec = 0; yieldtime.tv_nsec = 10 * 1000; pthread_cond_timedwait(&yield_cond, &yield_mutex, &yieldtime); break; } case yield_via_wait_signal: { int err; if ((err = pthread_mutex_lock(&yield_mutex))) { if (err != EDEADLK) fprintf(stderr,"pthread_mutex_lock yield_mutex %s\n", strerror(err)); } else if ((err = pthread_mutex_lock(&yield_sync))) { if (err != EDEADLK) fprintf(stderr,"pthread_mutex_lock yield_sync %s\n", strerror(err)); } else { sqLowLevelMFence(); if (yield && (err = pthread_cond_wait(&yield_cond, &yield_mutex))) fprintf(stderr,"pthread_cond_wait %s\n", strerror(err)); } break; } default: fprintf(stderr,"unrecognized yield method\n"); exit(5); } }