diff --git a/docs/src/guide/threads.rst b/docs/src/guide/threads.rst index 3b0a07e1..b6a4fd85 100644 --- a/docs/src/guide/threads.rst +++ b/docs/src/guide/threads.rst @@ -79,18 +79,21 @@ The mutex functions are a **direct** map to the pthread equivalents. .. literalinclude:: ../../../include/uv.h :lines: 1355-1360 -The ``uv_mutex_init()`` and ``uv_mutex_trylock()`` functions will return 0 on -success, and an error code otherwise. +The ``uv_mutex_init()``, ``uv_mutex_init_recursive()`` and ``uv_mutex_trylock()`` +functions will return 0 on success, and an error code otherwise. If `libuv` has been compiled with debugging enabled, ``uv_mutex_destroy()``, ``uv_mutex_lock()`` and ``uv_mutex_unlock()`` will ``abort()`` on error. Similarly ``uv_mutex_trylock()`` will abort if the error is anything *other than* ``EAGAIN`` or ``EBUSY``. -Recursive mutexes are supported by some platforms, but you should not rely on -them. The BSD mutex implementation will raise an error if a thread which has +Recursive mutexes are supported, but you should not rely on them. Also, they +should not be used with ``uv_cond_t`` variables. + +The default BSD mutex implementation will raise an error if a thread which has locked a mutex attempts to lock it again. For example, a construct like:: + uv_mutex_init(a_mutex); uv_mutex_lock(a_mutex); uv_thread_create(thread_id, entry, (void *)a_mutex); uv_mutex_lock(a_mutex); @@ -102,8 +105,7 @@ return an error in the second call to ``uv_mutex_lock()``. .. note:: - Mutexes on linux support attributes for a recursive mutex, but the API is - not exposed via libuv. + Mutexes on Windows are always recursive. Locks ~~~~~ diff --git a/docs/src/threading.rst b/docs/src/threading.rst index e876dde1..bca8ba1d 100644 --- a/docs/src/threading.rst +++ b/docs/src/threading.rst @@ -91,6 +91,7 @@ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. c:function:: int uv_mutex_init(uv_mutex_t* handle) +.. c:function:: int uv_mutex_init_recursive(uv_mutex_t* handle) .. c:function:: void uv_mutex_destroy(uv_mutex_t* handle) .. c:function:: void uv_mutex_lock(uv_mutex_t* handle) .. c:function:: int uv_mutex_trylock(uv_mutex_t* handle) diff --git a/include/uv.h b/include/uv.h index eac63dde..0e4151d1 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1424,6 +1424,7 @@ UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); +UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); diff --git a/src/unix/thread.c b/src/unix/thread.c index e61b51ed..abaca295 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -241,6 +241,25 @@ int uv_mutex_init(uv_mutex_t* mutex) { } +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + pthread_mutexattr_t attr; + int err; + + if (pthread_mutexattr_init(&attr)) + abort(); + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + abort(); + + err = pthread_mutex_init(mutex, &attr); + + if (pthread_mutexattr_destroy(&attr)) + abort(); + + return -err; +} + + void uv_mutex_destroy(uv_mutex_t* mutex) { if (pthread_mutex_destroy(mutex)) abort(); diff --git a/src/win/thread.c b/src/win/thread.c index 91684e93..30b2d779 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -198,6 +198,11 @@ int uv_mutex_init(uv_mutex_t* mutex) { } +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + return uv_mutex_init(mutex); +} + + void uv_mutex_destroy(uv_mutex_t* mutex) { DeleteCriticalSection(mutex); } diff --git a/test/test-list.h b/test/test-list.h index 6e84653e..0dde57c2 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -328,6 +328,7 @@ TEST_DECLARE (threadpool_cancel_single) TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_stack_size) TEST_DECLARE (thread_mutex) +TEST_DECLARE (thread_mutex_recursive) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_rwlock_trylock) TEST_DECLARE (thread_create) @@ -840,6 +841,7 @@ TASK_LIST_START TEST_ENTRY (thread_local_storage) TEST_ENTRY (thread_stack_size) TEST_ENTRY (thread_mutex) + TEST_ENTRY (thread_mutex_recursive) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_rwlock_trylock) TEST_ENTRY (thread_create) diff --git a/test/test-mutexes.c b/test/test-mutexes.c index af5e4e88..975222ca 100644 --- a/test/test-mutexes.c +++ b/test/test-mutexes.c @@ -50,6 +50,26 @@ TEST_IMPL(thread_mutex) { } +TEST_IMPL(thread_mutex_recursive) { + uv_mutex_t mutex; + int r; + + r = uv_mutex_init_recursive(&mutex); + ASSERT(r == 0); + + uv_mutex_lock(&mutex); + uv_mutex_lock(&mutex); + ASSERT(0 == uv_mutex_trylock(&mutex)); + + uv_mutex_unlock(&mutex); + uv_mutex_unlock(&mutex); + uv_mutex_unlock(&mutex); + uv_mutex_destroy(&mutex); + + return 0; +} + + TEST_IMPL(thread_rwlock) { uv_rwlock_t rwlock; int r;