
/* 
 * Please compile this for x86, even if you're running a 64-bit kernel
 * gcc -O2 -m32 crazylock.c -o crazylock
 */

#define _GNU_SOURCE 1
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef  DEFAULT_NR_THREADS
# define DEFAULT_NR_THREADS 3U
#endif
#ifndef  PIN_TO_CPU
# define PIN_TO_CPU 0
#endif

static pthread_rwlock_t  rwlock = PTHREAD_RWLOCK_INITIALIZER;
static size_t            fodder_size;
static unsigned char    *fodder;

static void
cache_get(const unsigned int n)
{
    assert(rwlock.__data.__nr_readers        <  n);
    assert(rwlock.__data.__nr_writers_queued <  n);
    pthread_rwlock_rdlock(&rwlock);
    assert(rwlock.__data.__nr_readers        <= n);
    assert(rwlock.__data.__nr_writers_queued <  n);
    
    fodder[rand() % fodder_size]++;
    
    pthread_rwlock_unlock(&rwlock);
    assert(rwlock.__data.__nr_readers        <  n);
    assert(rwlock.__data.__nr_writers_queued <  n);
}

static void *
thread_main(void *n_ptr)
{
    const unsigned int n = (const unsigned int) n_ptr;
    
    for (;;) {
        cache_get(n);       
    }
    /* NOTREACHED */
    return NULL;
}

static void
spawn_threads(pthread_t *threads, unsigned int n)
{
    unsigned int i = n;
    
    while (i-- > 0U) {  
        pthread_create(&threads[i], NULL, thread_main, (void *) n);

#if defined(CPU_SET) && PIN_TO_CPU != 0
        cpu_set_t cpuset;
        CPU_ZERO(&cpuset);
        (void) CPU_SET(i, &cpuset);
        pthread_setaffinity_np(threads[i], sizeof(cpuset), &cpuset);
#endif
    }
}

static void
usage(void)
{
    printf("Usage: crazylock <number of threads (default: %u)>\n",
           DEFAULT_NR_THREADS);
}

int
main(int argc, char *argv[])
{
    pthread_t    *threads;
    unsigned int  i;    
    unsigned int  n = DEFAULT_NR_THREADS;

    if (argc > 1) {
       n = (unsigned int) strtoul(argv[1], NULL, 10);
       if (n == 0U) {
           usage();
       }
    }   
    assert((threads = malloc((size_t) n * sizeof *threads)) != NULL);
    fodder_size = 1024U * 1024U * 1024U;
    assert((fodder = malloc(fodder_size)) != NULL);
    spawn_threads(threads, n);

    i = n;
    while (i-- > 0U) {
        pthread_join(threads[i], NULL);
    }
    /* NOTREACHED */
    free(threads);
   
    return 0;
}
