PThread RWLock - pthread_rwlock_unlock unlocks from wrong thread

Originator:gngrwzrd.lists
Number:rdar://8588290 Date Originated:10/24/2010
Status:Open Resolved:
Product:Mac OS X Product Version:10.6.5
Classification:Serious Bug Reproducible:Always
 
The pthread library behaves unexpectedly when mixing recursive read locks with write locks. Unbalanced unlock calls will unlock a different threads' lock.

Steps to Reproduce:

-Compile the below c file with gcc -Wall -pthread t2.c
-Run a.out

Expected Results:

The third unbalanced unlock call from the read thread should return EINVAL or an equivalent error. The unlock of the write thread should succeed.

Actual Results:

The third unbalanced unlock call from the read thread unlocks the write lock that the write thread currently holds. The first unlock call to the write thread fails because the unbalanced unlock from the read thread has already unlocked the write thread.

Code:

t2.c
//-----------------

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

//gcc -Wall -pthread t2.c

pthread_t t;
pthread_t t2;
pthread_rwlock_t m;

void * fn(void * arg) {
	int res;
	res = pthread_rwlock_rdlock(&m);
	fprintf(stderr,"read lock acquire 1 res: %i\n",res);
	res = pthread_rwlock_rdlock(&m);
	fprintf(stderr,"read lock acquire 2 res: %i\n",res);
	res = pthread_rwlock_unlock(&m);
	fprintf(stderr,"read lock release 1 res: %i\n",res);
	usleep( 10000);
	res = pthread_rwlock_unlock(&m);
	fprintf(stderr,"read lock release 2 res: %i\n",res);
	
	/** UNLOCKS THE WRITE LOCK (from a read thread) **/
	res = pthread_rwlock_unlock(&m);
	fprintf( stderr, "read lock release 3 res: %i\n", res);
	
	return NULL;
}

void * fn2(void * arg) {
	int res;
	res = pthread_rwlock_wrlock(&m);
	fprintf(stderr,"write lock acquire res: %i\n",res);
	usleep( 10000);
	res = pthread_rwlock_unlock(&m);
	fprintf(stderr,"write lock release res: %i\n",res);
	return NULL;
}

int main(int argc, char ** argv)
{
	pthread_rwlock_init( &m, NULL);
	pthread_create( &t, NULL, fn, NULL);
	usleep( 2000);
	pthread_create( &t2, NULL, fn2, NULL);
	pthread_join( t, NULL);
	pthread_join( t2, NULL);
	return 0;
}

//----------------------

Comments

Response from Apple

This is a response I got from Apple. They're right. The posix spec says the behavior is undefined if the calling thread doesn't own the lock.


The POSIX spec doesn't specify what's being claimed here. Spurious unlocks in mutex unlocks lock held by others except when mutex is of type PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_RECURSIVE. The NORMAL or DEFAULT another thread can unlock. Specifically the spec says "Results are undefined if the read-write lock rwlock is not held by the calling thread.".

Also the read lock can be held by many different threads, not just one thread as in this example. So maintaining all owners of read locks is not going to be possible. It is possible to ensure the writer lock is released by the writer but since the original implementation did not have that restriction we cannot add it now. That would be a compatibility change. And someone somewhere is depending on it. So I do not think we can do much about this now so this is not a bug in the implementation rather an enhancement request.

I guess that makes sense. Just stinks that their behavior doesn't match what linux does.

By gngrwzrd.lists at Oct. 26, 2010, 11:23 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!