unix,win: add uv_mutex_init_recursive()

Support the creation of recursive mutexes on Unix. A matching
API is added on Windows, however mutexes on Windows are always
recursive.

Refs: https://github.com/libuv/libuv/issues/1022
PR-URL: https://github.com/libuv/libuv/pull/1555
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Scott Parker 2017-09-21 10:36:20 -07:00 committed by cjihrig
parent 5b6eead064
commit ec96b55438
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
7 changed files with 56 additions and 6 deletions

View File

@ -79,18 +79,21 @@ The mutex functions are a **direct** map to the pthread equivalents.
.. literalinclude:: ../../../include/uv.h .. literalinclude:: ../../../include/uv.h
:lines: 1355-1360 :lines: 1355-1360
The ``uv_mutex_init()`` and ``uv_mutex_trylock()`` functions will return 0 on The ``uv_mutex_init()``, ``uv_mutex_init_recursive()`` and ``uv_mutex_trylock()``
success, and an error code otherwise. functions will return 0 on success, and an error code otherwise.
If `libuv` has been compiled with debugging enabled, ``uv_mutex_destroy()``, If `libuv` has been compiled with debugging enabled, ``uv_mutex_destroy()``,
``uv_mutex_lock()`` and ``uv_mutex_unlock()`` will ``abort()`` on error. ``uv_mutex_lock()`` and ``uv_mutex_unlock()`` will ``abort()`` on error.
Similarly ``uv_mutex_trylock()`` will abort if the error is anything *other Similarly ``uv_mutex_trylock()`` will abort if the error is anything *other
than* ``EAGAIN`` or ``EBUSY``. than* ``EAGAIN`` or ``EBUSY``.
Recursive mutexes are supported by some platforms, but you should not rely on Recursive mutexes are supported, but you should not rely on them. Also, they
them. The BSD mutex implementation will raise an error if a thread which has 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:: locked a mutex attempts to lock it again. For example, a construct like::
uv_mutex_init(a_mutex);
uv_mutex_lock(a_mutex); uv_mutex_lock(a_mutex);
uv_thread_create(thread_id, entry, (void *)a_mutex); uv_thread_create(thread_id, entry, (void *)a_mutex);
uv_mutex_lock(a_mutex); uv_mutex_lock(a_mutex);
@ -102,8 +105,7 @@ return an error in the second call to ``uv_mutex_lock()``.
.. note:: .. note::
Mutexes on linux support attributes for a recursive mutex, but the API is Mutexes on Windows are always recursive.
not exposed via libuv.
Locks Locks
~~~~~ ~~~~~

View File

@ -91,6 +91,7 @@ Functions return 0 on success or an error code < 0 (unless the
return type is void, of course). return type is void, of course).
.. c:function:: int uv_mutex_init(uv_mutex_t* handle) .. 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_destroy(uv_mutex_t* handle)
.. c:function:: void uv_mutex_lock(uv_mutex_t* handle) .. c:function:: void uv_mutex_lock(uv_mutex_t* handle)
.. c:function:: int uv_mutex_trylock(uv_mutex_t* handle) .. c:function:: int uv_mutex_trylock(uv_mutex_t* handle)

View File

@ -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 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(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_destroy(uv_mutex_t* handle);
UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle);
UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle);

View File

@ -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) { void uv_mutex_destroy(uv_mutex_t* mutex) {
if (pthread_mutex_destroy(mutex)) if (pthread_mutex_destroy(mutex))
abort(); abort();

View File

@ -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) { void uv_mutex_destroy(uv_mutex_t* mutex) {
DeleteCriticalSection(mutex); DeleteCriticalSection(mutex);
} }

View File

@ -328,6 +328,7 @@ TEST_DECLARE (threadpool_cancel_single)
TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_local_storage)
TEST_DECLARE (thread_stack_size) TEST_DECLARE (thread_stack_size)
TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_mutex)
TEST_DECLARE (thread_mutex_recursive)
TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_rwlock)
TEST_DECLARE (thread_rwlock_trylock) TEST_DECLARE (thread_rwlock_trylock)
TEST_DECLARE (thread_create) TEST_DECLARE (thread_create)
@ -840,6 +841,7 @@ TASK_LIST_START
TEST_ENTRY (thread_local_storage) TEST_ENTRY (thread_local_storage)
TEST_ENTRY (thread_stack_size) TEST_ENTRY (thread_stack_size)
TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_mutex)
TEST_ENTRY (thread_mutex_recursive)
TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_rwlock)
TEST_ENTRY (thread_rwlock_trylock) TEST_ENTRY (thread_rwlock_trylock)
TEST_ENTRY (thread_create) TEST_ENTRY (thread_create)

View File

@ -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) { TEST_IMPL(thread_rwlock) {
uv_rwlock_t rwlock; uv_rwlock_t rwlock;
int r; int r;