unix: work around glibc semaphore race condition
Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674 by providing a custom implementation for glibc < 2.21 in terms of other concurrency primitives. The glibc implementation on these versions is inherently unsafe. So, while libuv and Node.js support those versions, it seems to make sense for libuv in its functionality as a platform abstraction library to provide a working version. Fixes: https://github.com/nodejs/node/issues/19903 PR-URL: https://github.com/libuv/libuv/pull/1795 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
parent
19855c03ba
commit
fbd6de31be
@ -37,6 +37,10 @@
|
||||
#include <sys/sem.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <gnu/libc-version.h> /* gnu_get_libc_version() */
|
||||
#endif
|
||||
|
||||
#undef NANOSEC
|
||||
#define NANOSEC ((uint64_t) 1e9)
|
||||
|
||||
@ -502,26 +506,138 @@ int uv_sem_trywait(uv_sem_t* sem) {
|
||||
|
||||
#else /* !(defined(__APPLE__) && defined(__MACH__)) */
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
#ifdef __GLIBC__
|
||||
|
||||
/* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674
|
||||
* by providing a custom implementation for glibc < 2.21 in terms of other
|
||||
* concurrency primitives.
|
||||
* Refs: https://github.com/nodejs/node/issues/19903 */
|
||||
|
||||
/* To preserve ABI compatibility, we treat the uv_sem_t as storage for
|
||||
* a pointer to the actual struct we're using underneath. */
|
||||
|
||||
static uv_once_t glibc_version_check_once = UV_ONCE_INIT;
|
||||
static int glibc_needs_custom_semaphore = 0;
|
||||
|
||||
static void glibc_version_check(void) {
|
||||
const char* version = gnu_get_libc_version();
|
||||
glibc_needs_custom_semaphore =
|
||||
version[0] == '2' && version[1] == '.' &&
|
||||
atoi(version + 2) < 21;
|
||||
}
|
||||
|
||||
#else /* !defined(__GLIBC__) */
|
||||
|
||||
#define glibc_needs_custom_semaphore 0
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct uv_semaphore_s {
|
||||
uv_mutex_t mutex;
|
||||
uv_cond_t cond;
|
||||
unsigned int value;
|
||||
} uv_semaphore_t;
|
||||
|
||||
|
||||
STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*));
|
||||
|
||||
static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {
|
||||
int err;
|
||||
uv_semaphore_t* sem;
|
||||
|
||||
sem = uv__malloc(sizeof(*sem));
|
||||
if (sem == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
if ((err = uv_mutex_init(&sem->mutex)) != 0) {
|
||||
uv__free(sem);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = uv_cond_init(&sem->cond)) != 0) {
|
||||
uv_mutex_destroy(&sem->mutex);
|
||||
uv__free(sem);
|
||||
return err;
|
||||
}
|
||||
|
||||
sem->value = value;
|
||||
*(uv_semaphore_t**)sem_ = sem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__custom_sem_destroy(uv_sem_t* sem_) {
|
||||
uv_semaphore_t* sem;
|
||||
|
||||
sem = *(uv_semaphore_t**)sem_;
|
||||
uv_cond_destroy(&sem->cond);
|
||||
uv_mutex_destroy(&sem->mutex);
|
||||
uv__free(sem);
|
||||
}
|
||||
|
||||
|
||||
static void uv__custom_sem_post(uv_sem_t* sem_) {
|
||||
uv_semaphore_t* sem;
|
||||
|
||||
sem = *(uv_semaphore_t**)sem_;
|
||||
uv_mutex_lock(&sem->mutex);
|
||||
sem->value++;
|
||||
if (sem->value == 1)
|
||||
uv_cond_signal(&sem->cond);
|
||||
uv_mutex_unlock(&sem->mutex);
|
||||
}
|
||||
|
||||
|
||||
static void uv__custom_sem_wait(uv_sem_t* sem_) {
|
||||
uv_semaphore_t* sem;
|
||||
|
||||
sem = *(uv_semaphore_t**)sem_;
|
||||
uv_mutex_lock(&sem->mutex);
|
||||
while (sem->value == 0)
|
||||
uv_cond_wait(&sem->cond, &sem->mutex);
|
||||
sem->value--;
|
||||
uv_mutex_unlock(&sem->mutex);
|
||||
}
|
||||
|
||||
|
||||
static int uv__custom_sem_trywait(uv_sem_t* sem_) {
|
||||
uv_semaphore_t* sem;
|
||||
|
||||
sem = *(uv_semaphore_t**)sem_;
|
||||
if (uv_mutex_trylock(&sem->mutex) != 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
if (sem->value == 0) {
|
||||
uv_mutex_unlock(&sem->mutex);
|
||||
return UV_EAGAIN;
|
||||
}
|
||||
|
||||
sem->value--;
|
||||
uv_mutex_unlock(&sem->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uv__sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
if (sem_init(sem, 0, value))
|
||||
return UV__ERR(errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
static void uv__sem_destroy(uv_sem_t* sem) {
|
||||
if (sem_destroy(sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
static void uv__sem_post(uv_sem_t* sem) {
|
||||
if (sem_post(sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
static void uv__sem_wait(uv_sem_t* sem) {
|
||||
int r;
|
||||
|
||||
do
|
||||
@ -533,7 +649,7 @@ void uv_sem_wait(uv_sem_t* sem) {
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
static int uv__sem_trywait(uv_sem_t* sem) {
|
||||
int r;
|
||||
|
||||
do
|
||||
@ -549,6 +665,49 @@ int uv_sem_trywait(uv_sem_t* sem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
#ifdef __GLIBC__
|
||||
uv_once(&glibc_version_check_once, glibc_version_check);
|
||||
#endif
|
||||
|
||||
if (glibc_needs_custom_semaphore)
|
||||
return uv__custom_sem_init(sem, value);
|
||||
else
|
||||
return uv__sem_init(sem, value);
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
if (glibc_needs_custom_semaphore)
|
||||
uv__custom_sem_destroy(sem);
|
||||
else
|
||||
uv__sem_destroy(sem);
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
if (glibc_needs_custom_semaphore)
|
||||
uv__custom_sem_post(sem);
|
||||
else
|
||||
uv__sem_post(sem);
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
if (glibc_needs_custom_semaphore)
|
||||
uv__custom_sem_wait(sem);
|
||||
else
|
||||
uv__sem_wait(sem);
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
if (glibc_needs_custom_semaphore)
|
||||
return uv__custom_sem_trywait(sem);
|
||||
else
|
||||
return uv__sem_trywait(sem);
|
||||
}
|
||||
|
||||
#endif /* defined(__APPLE__) && defined(__MACH__) */
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user