rwlock.c   [plain text]


#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>

struct context {
	pthread_rwlock_t rwlock;
	long value;
	long count;
};

void *test_thread(void *ptr) {
	int res;
	long old;
	struct context *context = ptr;

	int i = 0;
	char *str;

	do {
		bool try = i & 1;
		bool exclusive = i & 2;
		switch (i++ & 3) {
			case 0:
				str = "pthread_rwlock_rdlock";
				res = pthread_rwlock_rdlock(&context->rwlock);
				break;
			case 1:
				str = "pthread_rwlock_tryrdlock";
				res = pthread_rwlock_tryrdlock(&context->rwlock);
				break;
			case 2:
				str = "pthread_rwlock_wrlock";
				res = pthread_rwlock_wrlock(&context->rwlock);
				break;
			case 3:
				str = "pthread_rwlock_trywrlock";
				res = pthread_rwlock_trywrlock(&context->rwlock);
				break;
		}
		if (res != 0) {
			if (try && res == EBUSY) {
				continue;
			}
			fprintf(stderr, "[%ld] %s: %s\n", context->count, str, strerror(res));
			abort();
		}

		if (exclusive) {
			old = __sync_fetch_and_or(&context->value, 1);
			if ((old & 1) != 0) {
				fprintf(stderr, "[%ld] OR %lx\n", context->count, old);
				abort();
			}
		}

		old = __sync_fetch_and_and(&context->value, 0);
		if ((old & 1) != (exclusive ? 1 : 0)) {
			fprintf(stderr, "[%ld] AND %lx\n", context->count, old);
			abort();
		}
	
		res = pthread_rwlock_unlock(&context->rwlock);
		if (res) {
			fprintf(stderr, "[%ld] pthread_rwlock_unlock: %s\n", context->count, strerror(res));
			abort();
		}
	} while (__sync_fetch_and_sub(&context->count, 1) > 0);

	return NULL;
}

int main(int argc, char *argv[])
{
	struct context context = {
		.rwlock = PTHREAD_RWLOCK_INITIALIZER,
		.value = 0,
		.count = 5000000,
	};
	int i;
	int res;
	int threads = 16;
	pthread_t p[threads];
	for (i = 0; i < threads; ++i) {
		res = pthread_create(&p[i], NULL, test_thread, &context);
		assert(res == 0);
	}
	for (i = 0; i < threads; ++i) {
		res = pthread_join(p[i], NULL);
		assert(res == 0);
	}

	return 0;
}