/* 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);

}
Exemple #3
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);
	}
}