win: fix unsavory rwlock fallback implementation
Before this patch an uv_mutex_t (backed by a critical section) could be released by a tread different from the thread that acquired it, which is not allowed. This is fixed by using a semaphore instead. Note that the affected code paths were used on Windows XP and Windows Server 2003 only. Fixes: https://github.com/libuv/libuv/issues/515 PR-URL: https://github.com/libuv/libuv/pull/516 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
parent
57b0a6da42
commit
3eb6764acd
@ -250,8 +250,16 @@ typedef union {
|
|||||||
/* windows.h. */
|
/* windows.h. */
|
||||||
SRWLOCK srwlock_;
|
SRWLOCK srwlock_;
|
||||||
struct {
|
struct {
|
||||||
uv_mutex_t read_mutex_;
|
union {
|
||||||
uv_mutex_t write_mutex_;
|
CRITICAL_SECTION cs;
|
||||||
|
/* TODO: remove me in v2.x. */
|
||||||
|
uv_mutex_t unused;
|
||||||
|
} read_lock_;
|
||||||
|
union {
|
||||||
|
HANDLE sem;
|
||||||
|
/* TODO: remove me in v2.x. */
|
||||||
|
uv_mutex_t unused;
|
||||||
|
} write_lock_;
|
||||||
unsigned int num_readers_;
|
unsigned int num_readers_;
|
||||||
} fallback_;
|
} fallback_;
|
||||||
} uv_rwlock_t;
|
} uv_rwlock_t;
|
||||||
|
|||||||
@ -396,18 +396,16 @@ static void uv__rwlock_srwlock_wrunlock(uv_rwlock_t* rwlock) {
|
|||||||
|
|
||||||
|
|
||||||
static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock) {
|
static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock) {
|
||||||
int err;
|
/* Initialize the semaphore that acts as the write lock. */
|
||||||
|
HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL);
|
||||||
|
if (handle == NULL)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
rwlock->fallback_.write_lock_.sem = handle;
|
||||||
|
|
||||||
err = uv_mutex_init(&rwlock->fallback_.read_mutex_);
|
/* Initialize the critical section protecting the reader count. */
|
||||||
if (err)
|
InitializeCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
return err;
|
|
||||||
|
|
||||||
err = uv_mutex_init(&rwlock->fallback_.write_mutex_);
|
|
||||||
if (err) {
|
|
||||||
uv_mutex_destroy(&rwlock->fallback_.read_mutex_);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Initialize the reader count. */
|
||||||
rwlock->fallback_.num_readers_ = 0;
|
rwlock->fallback_.num_readers_ = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -415,64 +413,88 @@ static int uv__rwlock_fallback_init(uv_rwlock_t* rwlock) {
|
|||||||
|
|
||||||
|
|
||||||
static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock) {
|
static void uv__rwlock_fallback_destroy(uv_rwlock_t* rwlock) {
|
||||||
uv_mutex_destroy(&rwlock->fallback_.read_mutex_);
|
DeleteCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
uv_mutex_destroy(&rwlock->fallback_.write_mutex_);
|
CloseHandle(rwlock->fallback_.write_lock_.sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock) {
|
static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock) {
|
||||||
uv_mutex_lock(&rwlock->fallback_.read_mutex_);
|
/* Acquire the lock that protects the reader count. */
|
||||||
|
EnterCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
|
|
||||||
if (++rwlock->fallback_.num_readers_ == 1)
|
/* Increase the reader count, and lock for write if this is the first
|
||||||
uv_mutex_lock(&rwlock->fallback_.write_mutex_);
|
* reader.
|
||||||
|
*/
|
||||||
|
if (++rwlock->fallback_.num_readers_ == 1) {
|
||||||
|
DWORD r = WaitForSingleObject(rwlock->fallback_.write_lock_.sem, INFINITE);
|
||||||
|
if (r != WAIT_OBJECT_0)
|
||||||
|
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||||
|
}
|
||||||
|
|
||||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
/* Release the lock that protects the reader count. */
|
||||||
|
LeaveCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock) {
|
static int uv__rwlock_fallback_tryrdlock(uv_rwlock_t* rwlock) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = uv_mutex_trylock(&rwlock->fallback_.read_mutex_);
|
if (!TryEnterCriticalSection(&rwlock->fallback_.read_lock_.cs))
|
||||||
if (err)
|
return UV_EAGAIN;
|
||||||
goto out;
|
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
if (rwlock->fallback_.num_readers_ == 0)
|
if (rwlock->fallback_.num_readers_ == 0) {
|
||||||
err = uv_mutex_trylock(&rwlock->fallback_.write_mutex_);
|
DWORD r = WaitForSingleObject(rwlock->fallback_.write_lock_.sem, 0);
|
||||||
|
if (r == WAIT_OBJECT_0)
|
||||||
|
rwlock->fallback_.num_readers_++;
|
||||||
|
else if (r == WAIT_TIMEOUT)
|
||||||
|
err = UV_EAGAIN;
|
||||||
|
else if (r == WAIT_FAILED)
|
||||||
|
err = uv_translate_sys_error(GetLastError());
|
||||||
|
else
|
||||||
|
err = UV_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (err == 0)
|
LeaveCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
rwlock->fallback_.num_readers_++;
|
|
||||||
|
|
||||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock) {
|
static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock) {
|
||||||
uv_mutex_lock(&rwlock->fallback_.read_mutex_);
|
EnterCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
|
|
||||||
if (--rwlock->fallback_.num_readers_ == 0)
|
if (--rwlock->fallback_.num_readers_ == 0) {
|
||||||
uv_mutex_unlock(&rwlock->fallback_.write_mutex_);
|
if (!ReleaseSemaphore(rwlock->fallback_.write_lock_.sem, 1, NULL))
|
||||||
|
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||||
|
}
|
||||||
|
|
||||||
uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
|
LeaveCriticalSection(&rwlock->fallback_.read_lock_.cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock) {
|
static void uv__rwlock_fallback_wrlock(uv_rwlock_t* rwlock) {
|
||||||
uv_mutex_lock(&rwlock->fallback_.write_mutex_);
|
DWORD r = WaitForSingleObject(rwlock->fallback_.write_lock_.sem, INFINITE);
|
||||||
|
if (r != WAIT_OBJECT_0)
|
||||||
|
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock) {
|
static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock) {
|
||||||
return uv_mutex_trylock(&rwlock->fallback_.write_mutex_);
|
DWORD r = WaitForSingleObject(rwlock->fallback_.write_lock_.sem, 0);
|
||||||
|
if (r == WAIT_OBJECT_0)
|
||||||
|
return 0;
|
||||||
|
else if (r == WAIT_TIMEOUT)
|
||||||
|
return UV_EAGAIN;
|
||||||
|
else if (r == WAIT_FAILED)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
else
|
||||||
|
return UV_EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock) {
|
static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock) {
|
||||||
uv_mutex_unlock(&rwlock->fallback_.write_mutex_);
|
if (!ReleaseSemaphore(rwlock->fallback_.write_lock_.sem, 1, NULL))
|
||||||
|
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user