Skip to content

Instantly share code, notes, and snippets.

@mqudsi
Created September 27, 2022 20:39
Show Gist options
  • Save mqudsi/5aac71d8177866ba2762bda01a3ff21a to your computer and use it in GitHub Desktop.
Save mqudsi/5aac71d8177866ba2762bda01a3ff21a to your computer and use it in GitHub Desktop.
Demonstration of unsafety of pthread_mutex_unlock
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static pthread_mutex_t mutex;
static void* thread1_entry(void * _arg) {
int *result = malloc(sizeof(int));
// Lock the mutex
if (pthread_mutex_lock(&mutex) != 0) {
perror("t1 pthread_mutex_lock");
*result = 1;
return result;
}
fprintf(stdout, "Thread 1: entered mutex\n");
sleep(2);
// Unlock the mutex
if (pthread_mutex_unlock(&mutex) != 0) {
perror("t1 pthread_mutex_unlock");
*result = 2;
return result;
}
fprintf(stdout, "Thread 1: doing sensitive work that should be in a mutex\n");
sleep(1);
fprintf(stdout, "Thread 1: finished work and unlocked mutex OK\n");
*result = 0;
return result;
}
int main() {
int result;
pthread_mutex_init(&mutex, NULL);
pthread_t thread1_id;
pthread_create(&thread1_id, NULL, thread1_entry, NULL);
// Sleep long enough for thread 1 to obtain the mutex
// but not so long that it leaves it.
usleep(500 * 1000); // 500 ms
// Verify that we can't enter the mutex while it's locked
if (pthread_mutex_trylock(&mutex) == 0) {
fprintf(stderr, "Thread 2 was able to obtain the mutex while it was locked!\n");
result = 1;
}
// Now unlock the mutex even though thread 1 currently
// has ownership of it.
if (pthread_mutex_unlock(&mutex) != 0) {
perror("t2 pthread_mutex_unlock");
printf("Thread 2 couldn't unlock a thread it didn't own. This is good!\n");
result = 0;
} else {
fprintf(stderr, "Thread 2: unlocked the mutex from under thread 1! This is bad!\n");
result = 1;
}
if (pthread_mutex_trylock(&mutex) == 0) {
fprintf(stderr, "Thread 2: obtained the mutex even though thread 1 is still using it!\n");
result = 1;
if (pthread_mutex_unlock(&mutex) != 0) {
perror("t2 pthread_mutex_unlock 2");
}
}
int *thread1_exit;
pthread_join(thread1_id, (void **) &thread1_exit);
if (*thread1_exit == 0) {
printf("Thread 1 terminated OK even though the rug was pulled out from under it!\n");
return 1;
} else {
fprintf(stderr, "Thread 1 terminated with an error, probably trying to unlock the mutex again.\n");
return result;
}
}
@mqudsi
Copy link
Author

mqudsi commented Sep 27, 2022

On Linux, this terminates with the following output:

Thread 1: entered mutex
Thread 2: unlocked the mutex from under thread 1! This is bad!
Thread 2: obtained the mutex even though thread 1 is still using it!
Thread 1: doing sensitive work that should be in a mutex
Thread 1: finished work and unlocked mutex OK
Thread 1 terminated OK even though the rug was pulled out from under it!


...Program finished with exit code 1
Press ENTER to exit console.

You can try it yourself here: https://onlinegdb.com/9hmNhBuEc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment