Change spinlock to use gcc atomic function when available; enhanced spinlock tests.
This commit is contained in:
		@ -30,6 +30,7 @@
 | 
			
		||||
 | 
			
		||||
#include <spinlock.h>
 | 
			
		||||
#include <atomic.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialise a spinlock.
 | 
			
		||||
@ -39,13 +40,13 @@
 | 
			
		||||
void
 | 
			
		||||
spinlock_init(SPINLOCK *lock)
 | 
			
		||||
{
 | 
			
		||||
	lock->lock = 0;
 | 
			
		||||
    lock->lock = 0;
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
	lock->spins = 0;
 | 
			
		||||
	lock->acquired = 0;
 | 
			
		||||
	lock->waiting = 0;
 | 
			
		||||
	lock->max_waiting = 0;
 | 
			
		||||
	lock->contended = 0;
 | 
			
		||||
    lock->spins = 0;
 | 
			
		||||
    lock->acquired = 0;
 | 
			
		||||
    lock->waiting = 0;
 | 
			
		||||
    lock->max_waiting = 0;
 | 
			
		||||
    lock->contended = 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -62,24 +63,30 @@ int	spins = 0;
 | 
			
		||||
 | 
			
		||||
	atomic_add(&(lock->waiting), 1);
 | 
			
		||||
#endif
 | 
			
		||||
	while (atomic_add(&(lock->lock), 1) != 0)
 | 
			
		||||
	{
 | 
			
		||||
		atomic_add(&(lock->lock), -1);
 | 
			
		||||
        
 | 
			
		||||
#ifdef __GNUC__
 | 
			
		||||
    while (__sync_lock_test_and_set(&(lock->lock), 1))
 | 
			
		||||
        while (lock->lock) {
 | 
			
		||||
#else    
 | 
			
		||||
    while (atomic_add(&(lock->lock), 1) != 0)
 | 
			
		||||
    {
 | 
			
		||||
	atomic_add(&(lock->lock), -1);
 | 
			
		||||
#endif
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
		atomic_add(&(lock->spins), 1);
 | 
			
		||||
		spins++;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
	if (spins)
 | 
			
		||||
	{
 | 
			
		||||
		lock->contended++;
 | 
			
		||||
		if (lock->maxspins < spins)
 | 
			
		||||
			lock->maxspins = spins;
 | 
			
		||||
	}
 | 
			
		||||
	lock->acquired++;
 | 
			
		||||
	lock->owner = THREAD_SHELF();
 | 
			
		||||
	atomic_add(&(lock->waiting), -1);
 | 
			
		||||
    if (spins)
 | 
			
		||||
    {
 | 
			
		||||
        lock->contended++;
 | 
			
		||||
        if (lock->maxspins < spins)
 | 
			
		||||
            lock->maxspins = spins;
 | 
			
		||||
    }
 | 
			
		||||
    lock->acquired++;
 | 
			
		||||
    lock->owner = THREAD_SHELF();
 | 
			
		||||
    atomic_add(&(lock->waiting), -1);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -92,16 +99,20 @@ int	spins = 0;
 | 
			
		||||
int
 | 
			
		||||
spinlock_acquire_nowait(SPINLOCK *lock)
 | 
			
		||||
{
 | 
			
		||||
	if (atomic_add(&(lock->lock), 1) != 0)
 | 
			
		||||
	{
 | 
			
		||||
		atomic_add(&(lock->lock), -1);
 | 
			
		||||
		return FALSE;
 | 
			
		||||
	}
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
	lock->acquired++;
 | 
			
		||||
	lock->owner = THREAD_SHELF();
 | 
			
		||||
#ifdef __GNUC__
 | 
			
		||||
    if (__sync_lock_test_and_set(&(lock->lock), 1)) return FALSE;
 | 
			
		||||
#else
 | 
			
		||||
    if (atomic_add(&(lock->lock), 1) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        atomic_add(&(lock->lock), -1);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
	return TRUE;
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
    lock->acquired++;
 | 
			
		||||
    lock->owner = THREAD_SHELF();
 | 
			
		||||
#endif
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -112,11 +123,16 @@ spinlock_acquire_nowait(SPINLOCK *lock)
 | 
			
		||||
void
 | 
			
		||||
spinlock_release(SPINLOCK *lock)
 | 
			
		||||
{
 | 
			
		||||
#if SPINLOCK_PROFILE
 | 
			
		||||
	if (lock->waiting > lock->max_waiting)
 | 
			
		||||
		lock->max_waiting = lock->waiting;
 | 
			
		||||
 #if SPINLOCK_PROFILE
 | 
			
		||||
    if (lock->waiting > lock->max_waiting)
 | 
			
		||||
        lock->max_waiting = lock->waiting;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef __GNUC__
 | 
			
		||||
    __sync_synchronize(); // Memory barrier.
 | 
			
		||||
    lock->lock = 0;
 | 
			
		||||
#else
 | 
			
		||||
    atomic_add(&(lock->lock), -1);
 | 
			
		||||
#endif
 | 
			
		||||
	atomic_add(&(lock->lock), -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -105,12 +105,16 @@ test2()
 | 
			
		||||
{
 | 
			
		||||
SPINLOCK	lck;
 | 
			
		||||
void		*handle;
 | 
			
		||||
struct timespec sleeptime;
 | 
			
		||||
 | 
			
		||||
    sleeptime.tv_sec = 10;
 | 
			
		||||
    sleeptime.tv_nsec = 0;
 | 
			
		||||
 | 
			
		||||
	acquire_time = 0;
 | 
			
		||||
	spinlock_init(&lck);
 | 
			
		||||
	spinlock_acquire(&lck);
 | 
			
		||||
	handle = thread_start(test2_helper, (void *)&lck);
 | 
			
		||||
	sleep(10);
 | 
			
		||||
	nanosleep(&sleeptime, NULL);
 | 
			
		||||
	spinlock_release(&lck);
 | 
			
		||||
	thread_wait(handle);
 | 
			
		||||
 | 
			
		||||
@ -122,12 +126,118 @@ void		*handle;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main(int argc, char **argv)
 | 
			
		||||
/**
 | 
			
		||||
 * test3	spinlock_acquire tests process bound threads
 | 
			
		||||
 *
 | 
			
		||||
 * Check that spinlock correctly blocks all other threads whilst the spinlock
 | 
			
		||||
 * is held.
 | 
			
		||||
 *
 | 
			
		||||
 * Start multiple threads that obtain spinlock and run process bound
 | 
			
		||||
 */
 | 
			
		||||
#define THREADS 5
 | 
			
		||||
#define ITERATIONS 25000
 | 
			
		||||
#define PROCESS_LOOP 10000
 | 
			
		||||
#define SECONDS 15
 | 
			
		||||
#define NANOTIME 100000
 | 
			
		||||
 | 
			
		||||
static int  times_run, failures;
 | 
			
		||||
static volatile int active;
 | 
			
		||||
static int  threadrun[THREADS];
 | 
			
		||||
static int  nowait[THREADS];
 | 
			
		||||
static SPINLOCK lck;
 | 
			
		||||
static void
 | 
			
		||||
test3_helper(void *data)
 | 
			
		||||
{
 | 
			
		||||
// SPINLOCK   *lck = (SPINLOCK *)data;
 | 
			
		||||
int         i;
 | 
			
		||||
int         n = *(int *)data;
 | 
			
		||||
struct timespec sleeptime;
 | 
			
		||||
time_t          rawtime;
 | 
			
		||||
 | 
			
		||||
    sleeptime.tv_sec = 0;
 | 
			
		||||
    sleeptime.tv_nsec = 1;
 | 
			
		||||
 | 
			
		||||
    while (1) {
 | 
			
		||||
        if (spinlock_acquire_nowait(&lck)) {
 | 
			
		||||
            nowait[n]++;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            spinlock_acquire(&lck);
 | 
			
		||||
        }
 | 
			
		||||
        if (times_run++ > ITERATIONS) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        threadrun[n]++;
 | 
			
		||||
        /*
 | 
			
		||||
        if (99 == (times_run % 100)) {
 | 
			
		||||
            time ( &rawtime );
 | 
			
		||||
            fprintf(stderr, "%s Done %d iterations of test, in thread %d.\n", asctime (localtime ( &rawtime )), times_run, n);
 | 
			
		||||
        }
 | 
			
		||||
         */
 | 
			
		||||
        if (0 != active) {
 | 
			
		||||
            fprintf(stderr, "spinlock: test 3 failed with active non-zero after lock obtained.\n");
 | 
			
		||||
            failures++;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            active = 1;
 | 
			
		||||
            for (i=0; i<PROCESS_LOOP; i++);
 | 
			
		||||
        }
 | 
			
		||||
        active = 0;
 | 
			
		||||
        spinlock_release(&lck);
 | 
			
		||||
        for (i=0; i<(8*PROCESS_LOOP); i++);
 | 
			
		||||
        // nanosleep(&sleeptime, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    spinlock_release(&lck);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
test3()
 | 
			
		||||
{
 | 
			
		||||
// SPINLOCK	lck;
 | 
			
		||||
void		*handle[THREADS];
 | 
			
		||||
int             i;
 | 
			
		||||
int             tnum[THREADS];
 | 
			
		||||
time_t          rawtime;
 | 
			
		||||
 | 
			
		||||
struct timespec sleeptime;
 | 
			
		||||
 | 
			
		||||
    sleeptime.tv_sec = 20;
 | 
			
		||||
    sleeptime.tv_nsec = NANOTIME;
 | 
			
		||||
 | 
			
		||||
    times_run = 0;
 | 
			
		||||
    active = 0;
 | 
			
		||||
    failures = 0;
 | 
			
		||||
    spinlock_init(&lck);
 | 
			
		||||
    time ( &rawtime );
 | 
			
		||||
    fprintf(stderr, "%s Starting %d threads.\n", asctime (localtime ( &rawtime )), THREADS);
 | 
			
		||||
    for (i = 0; i<THREADS; i++) {
 | 
			
		||||
        threadrun[i] = 0;
 | 
			
		||||
        tnum[i] = i;
 | 
			
		||||
        handle[i] = thread_start(test3_helper, &tnum[i]);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i<THREADS; i++) {
 | 
			
		||||
        fprintf(stderr, "spinlock_test 3 thread %d ran %d times, no wait %d times before waits.\n", i, threadrun[i], nowait[i]);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i<THREADS; i++) {
 | 
			
		||||
        time ( &rawtime );
 | 
			
		||||
        fprintf(stderr, "%s spinlock_test 3 finished sleeps, about to wait for thread %d.\n", asctime (localtime ( &rawtime )), i);
 | 
			
		||||
        thread_wait(handle[i]);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i<THREADS; i++) {
 | 
			
		||||
        fprintf(stderr, "spinlock_test 3 thread %d ran %d times, no wait %d times.\n", i, threadrun[i], nowait[i]);
 | 
			
		||||
    }
 | 
			
		||||
    time ( &rawtime );
 | 
			
		||||
    fprintf(stderr, "%s spinlock_test 3 completed, %d failures.\n", asctime (localtime ( &rawtime )), failures);
 | 
			
		||||
    return 0 == failures ? 0: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
int	result = 0;
 | 
			
		||||
 | 
			
		||||
	result += test1();
 | 
			
		||||
	result += test2();
 | 
			
		||||
        result += test3();
 | 
			
		||||
 | 
			
		||||
	exit(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user