Skip to content

Instantly share code, notes, and snippets.

@ChunMinChang
Last active April 20, 2017 07:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChunMinChang/fe46f3760e2230c09675e258ae1cf8eb to your computer and use it in GitHub Desktop.
Save ChunMinChang/fe46f3760e2230c09675e258ae1cf8eb to your computer and use it in GitHub Desktop.
Play pthread

pthread

Sample code for demonstrating different behavior in different pthread type

  • PTHREAD_MUTEX_NORMAL
    • This type of mutex does not detect deadlock
    • A thread attempting to relock this mutex without first unlocking it shall deadlock
    • A thread attempting to unlock a mutex locked by a different thread results in undefined behavior
    • A thread attempting to unlock an unlocked mutex results in undefined behavior
  • PTHREAD_MUTEX_ERRORCHECK
    • This type of mutex provides error checking
    • A thread attempting to relock this mutex without first unlocking it shall return with an error
    • A thread attempting to unlock a mutex which another thread has locked shall return with an error
    • A thread attempting to unlock an unlocked mutex shall return with an error
  • PTHREAD_MUTEX_RECURSIVE
    • A thread attempting to relock this mutex without first unlocking it shall succeed in locking the mutex
      • The relocking is allowd only in the same thread
      • The relocking deadlock which can occur with mutexes of type PTHREAD_MUTEX_NORMAL cannot occur with this type of mutex
      • Multiple locks of this mutex shall require the same number of unlocks to release the mutex before another thread can acquire the mutex
    • A thread attempting to unlock a mutex which another thread has locked shall return with an error
    • A thread attempting to unlock an unlocked mutex shall return with an error
  • PTHREAD_MUTEX_DEFAULT
    • Its default value is set to PTHREAD_MUTEX_NORMAL
    • It's allowed to map its value to other types

Scenarios and results

We have four scenarios:

  1. Lock a locked mutex in same thread
  2. Unlock a unlocked mutex
  3. (Wait to) Lock a locked mutex owned by another thread (normal use case)
  4. Unlock a locked mutex owned by another thread

Unlocking a mutex owned by current thread is definitely ok, so it's exclusive in our scenarios.

The results of our scenarios in different type is:

1 2 3 4
NORMAL ! v v v
ERRORCHECK v
RECURSIVE v v
DEFAULT ! v v v
  • !: It works without any error, but it will be self-deadlocked
  • v: It works without any error
  • empty: It can not works, with an error returned

TODO

  • Sample code for pthread_kill and pthread_detach

Note

pthread_mutex_trylock

The pthread_mutex_trylock() shall be equivalent to pthread_mutex_lock(), except that if the mutex object referenced by mutex is currently locked (by any thread, including the current thread), the call shall return immediately.

CC=gcc
CFLAGS=-Wall
SOURCES=pthread_default.c\
pthread_errorcheck.c\
pthread_normal.c\
pthread_recursive.c
EXECUTABLES=$(SOURCES:.c=)
all: $(EXECUTABLES)
.cpp:
$(CC) $(CFLAGS) $< -o $@
clean:
rm $(EXECUTABLES)
#include <assert.h>
#include <errno.h> // for error value returned from the pthread functions
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> // for uint64_t, sleep
pthread_mutex_t mutex_default;
// Return current thread id if th is NULL
uint64_t get_thread_id(pthread_t th)
{
uint64_t tid;
pthread_threadid_np(th, &tid);
return tid;
}
void* lock1(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_default));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] sleep ...\n", tid);
sleep(1);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_default));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* lock2(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_default));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_default));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* out_of_main_thread_unlock(void* arg)
{
// Unlock a mutex owned by other thread is ok if mutex is set to DEFAULT!
assert(!pthread_mutex_unlock(&mutex_default));
return NULL;
}
int main()
{
// PTHREAD_MUTEX_DEFAULT: Its default value is PTHREAD_MUTEX_NORMAL.
// It's allowed to map this mutex to one of
// the other mutex types.
pthread_mutexattr_t attr_default;
assert(!pthread_mutexattr_init(&attr_default));
assert(!pthread_mutexattr_settype(&attr_default, PTHREAD_MUTEX_DEFAULT));
assert(!pthread_mutex_init(&mutex_default, &attr_default));
assert(!pthread_mutexattr_destroy(&attr_default));
// Lock a locked mutex in same thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_default));
// EBUSY: The mutex could not be acquired because it was already locked.
assert(pthread_mutex_trylock(&mutex_default) == EBUSY);
// Program will be pending(self-deadlocked) when we lock a locked mutex here.
// assert(!pthread_mutex_lock(&mutex_default));
//
// self-deadlock:
//
// +---------------+
// | holds |
// v |
// main thread mutex
// | ^
// | requests |
// +---------------+
//
assert(!pthread_mutex_unlock(&mutex_default));
// Unlock a unlocked mutex
// -----------------------------------
// Unlock an plain(unlocked) mutex is ok if mutex is set to DEFAULT.
assert(!pthread_mutex_unlock(&mutex_default));
// Lock a mutex locked by another thread (normal case)
// -----------------------------------
pthread_t locker1, locker2;
pthread_create(&locker1, NULL, lock1, NULL);
pthread_create(&locker2, NULL, lock2, NULL);
pthread_join(locker1, NULL);
pthread_join(locker2, NULL);
// Unlock a mutex locked by another thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_default));
pthread_t out_of_main_thread_unlocker;
pthread_create(&out_of_main_thread_unlocker, NULL,
out_of_main_thread_unlock, NULL);
pthread_join(out_of_main_thread_unlocker, NULL);
// This is useless calling because mutex is already unlocked
// in out_of_main_thread_unlock.
// assert(!pthread_mutex_unlock(&mutex_default));
return 0;
}
#include <assert.h>
#include <errno.h> // for error value returned from the pthread functions
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> // for uint64_t, sleep
pthread_mutex_t mutex_errorcheck;
// Return current thread id if th is NULL
uint64_t get_thread_id(pthread_t th)
{
uint64_t tid;
pthread_threadid_np(th, &tid);
return tid;
}
void* lock1(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_errorcheck));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] sleep ...\n", tid);
sleep(1);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_errorcheck));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* lock2(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_errorcheck));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_errorcheck));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* out_of_main_thread_unlock(void* arg)
{
// We cannot unlock a mutex owned by other thread!
assert(pthread_mutex_unlock(&mutex_errorcheck) == EPERM);
return NULL;
}
int main()
{
// PTHREAD_MUTEX_ERRORCHECK: This type of mutex provides error checking.
pthread_mutexattr_t attr_errorcheck;
assert(!pthread_mutexattr_init(&attr_errorcheck));
assert(!pthread_mutexattr_settype(&attr_errorcheck, PTHREAD_MUTEX_ERRORCHECK));
assert(!pthread_mutex_init(&mutex_errorcheck, &attr_errorcheck));
assert(!pthread_mutexattr_destroy(&attr_errorcheck));
// Lock a locked mutex in same thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_errorcheck));
// EBUSY: The mutex could not be acquired because it was already locked.
assert(pthread_mutex_trylock(&mutex_errorcheck) == EBUSY);
// EDEADLK: The current thread already owns the mutex(it's already locked above).
// It could be used to check the mutex is owned by the current thread.
assert(pthread_mutex_lock(&mutex_errorcheck) == EDEADLK);
assert(!pthread_mutex_unlock(&mutex_errorcheck));
// Unlock a unlocked mutex
// -----------------------------------
// EPERM: The current thread does not own the mutex(it's already freed above).
assert(pthread_mutex_unlock(&mutex_errorcheck) == EPERM);
// Lock a mutex locked by another thread (normal case)
// -----------------------------------
pthread_t locker1, locker2;
pthread_create(&locker1, NULL, lock1, NULL);
pthread_create(&locker2, NULL, lock2, NULL);
pthread_join(locker1, NULL);
pthread_join(locker2, NULL);
// Unlock a mutex locked by another thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_errorcheck));
pthread_t out_of_main_thread_unlocker;
pthread_create(&out_of_main_thread_unlocker, NULL,
out_of_main_thread_unlock, NULL);
pthread_join(out_of_main_thread_unlocker, NULL);
assert(!pthread_mutex_unlock(&mutex_errorcheck));
return 0;
}
#include <assert.h>
#include <errno.h> // for error value returned from the pthread functions
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> // for uint64_t, sleep
pthread_mutex_t mutex_normal;
// Return current thread id if th is NULL
uint64_t get_thread_id(pthread_t th)
{
uint64_t tid;
pthread_threadid_np(th, &tid);
return tid;
}
void* lock1(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_normal));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] sleep ...\n", tid);
sleep(1);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_normal));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* lock2(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_normal));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_normal));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* out_of_main_thread_unlock(void* arg)
{
// Unlock a mutex owned by other thread is ok if mutex is set to NORMAL!
assert(!pthread_mutex_unlock(&mutex_normal));
return NULL;
}
int main()
{
// PTHREAD_MUTEX_NORMAL: This type of mutex does not detect deadlock.
pthread_mutexattr_t attr_normal;
assert(!pthread_mutexattr_init(&attr_normal));
assert(!pthread_mutexattr_settype(&attr_normal, PTHREAD_MUTEX_NORMAL));
assert(!pthread_mutex_init(&mutex_normal, &attr_normal));
assert(!pthread_mutexattr_destroy(&attr_normal));
// Lock a locked mutex in same thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_normal));
// EBUSY: The mutex could not be acquired because it was already locked.
assert(pthread_mutex_trylock(&mutex_normal) == EBUSY);
// Program will be pending(self-deadlocked) when we lock a locked mutex here.
// assert(!pthread_mutex_lock(&mutex_normal));
//
// self-deadlock:
//
// +---------------+
// | holds |
// v |
// main thread mutex
// | ^
// | requests |
// +---------------+
//
assert(!pthread_mutex_unlock(&mutex_normal));
// Unlock a unlocked mutex
// -----------------------------------
// Unlock an plain(unlocked) mutex is ok if mutex is set to NORMAL.
assert(!pthread_mutex_unlock(&mutex_normal));
// Lock a mutex locked by another thread (normal case)
// -----------------------------------
pthread_t locker1, locker2;
pthread_create(&locker1, NULL, lock1, NULL);
pthread_create(&locker2, NULL, lock2, NULL);
pthread_join(locker1, NULL);
pthread_join(locker2, NULL);
// Unlock a mutex locked by another thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_normal));
pthread_t out_of_main_thread_unlocker;
pthread_create(&out_of_main_thread_unlocker, NULL,
out_of_main_thread_unlock, NULL);
pthread_join(out_of_main_thread_unlocker, NULL);
// This is useless calling because mutex is already unlocked
// in out_of_main_thread_unlock.
// assert(!pthread_mutex_unlock(&mutex_normal));
return 0;
}
#include <assert.h>
#include <errno.h> // for error value returned from the pthread functions
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> // for uint64_t, sleep
pthread_mutex_t mutex_recursive;
// Return current thread id if th is NULL
uint64_t get_thread_id(pthread_t th)
{
uint64_t tid;
pthread_threadid_np(th, &tid);
return tid;
}
void* lock1(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
assert(!pthread_mutex_lock(&mutex_recursive));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] sleep ...\n", tid);
sleep(1);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_recursive));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* lock2(void* arg)
{
uint64_t tid = get_thread_id(NULL);
fprintf(stderr, "Thread %llu is created\n", tid);
// When this thread is executed after lock1's thread,
// it need to wait to lock the mutex held by lock1's thread,
// even the mutex is recursively-lockable.
// The recursively-lockable mutex can only be locked multiple times
// in the same thread. A thread attempting to lock a mutex
// owned by different thread still need to wait for locking!
assert(!pthread_mutex_lock(&mutex_recursive));
fprintf(stderr, "[%llu] mutex is locked\n", tid);
fprintf(stderr, "[%llu] Do something ...\n", tid);
assert(!pthread_mutex_unlock(&mutex_recursive));
fprintf(stderr, "[%llu] mutex is unlocked\n", tid);
return NULL;
}
void* out_of_main_thread_unlock(void* arg)
{
// We cannot unlock a mutex owned by other thread!
assert(pthread_mutex_unlock(&mutex_recursive) == EPERM);
return NULL;
}
int main()
{
// PTHREAD_MUTEX_RECURSIVE: This type of mutex could lock a mutex recusively.
pthread_mutexattr_t attr_recursive;
assert(!pthread_mutexattr_init(&attr_recursive));
assert(!pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE));
assert(!pthread_mutex_init(&mutex_recursive, &attr_recursive));
assert(!pthread_mutexattr_destroy(&attr_recursive));
// Lock a locked mutex in same thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_recursive));
// Trylock returns no error, so it means that we could lock it twice.
assert(!pthread_mutex_trylock(&mutex_recursive));
assert(!pthread_mutex_lock(&mutex_recursive));
assert(!pthread_mutex_unlock(&mutex_recursive)); // unlock first lock()
assert(!pthread_mutex_unlock(&mutex_recursive)); // unlock trylock()
assert(!pthread_mutex_unlock(&mutex_recursive)); // unlock second lock()
// Unlock a unlocked mutex
// -----------------------------------
// EPERM: The current thread does not own the mutex(it's already freed above).
assert(pthread_mutex_unlock(&mutex_recursive) == EPERM);
// Lock a mutex locked by another thread (normal case)
// -----------------------------------
pthread_t locker1, locker2;
pthread_create(&locker1, NULL, lock1, NULL);
pthread_create(&locker2, NULL, lock2, NULL);
pthread_join(locker1, NULL);
pthread_join(locker2, NULL);
// Unlock a mutex locked by another thread
// -----------------------------------
assert(!pthread_mutex_lock(&mutex_recursive));
pthread_t out_of_main_thread_unlocker;
pthread_create(&out_of_main_thread_unlocker, NULL,
out_of_main_thread_unlock, NULL);
pthread_join(out_of_main_thread_unlocker, NULL);
assert(!pthread_mutex_unlock(&mutex_recursive));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment