245 lines
5.3 KiB
C
245 lines
5.3 KiB
C
|
|
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) NGINX, Inc.
|
|
*/
|
|
|
|
#include <nxt_main.h>
|
|
|
|
|
|
#if (NXT_HAVE_SEM_TIMEDWAIT)
|
|
|
|
/*
|
|
* Linux POSIX semaphores use atomic/futex operations in since glibc 2.3.
|
|
*
|
|
* FreeBSD has two POSIX semaphore implementations. The first implementation
|
|
* has been introduced in FreeBSD 5.0 but it has some drawbacks:
|
|
* 1) it had a bug (http://bugs.freebsd.org/127545) fixed in FreeBSD 7.2;
|
|
* 2) it does not use atomic operations and always calls ksem syscalls;
|
|
* 3) a number of semaphores is just 30 by default and until FreeBSD 8.1
|
|
* the number cannot be changed after boot time.
|
|
*
|
|
* The second implementation has been introduced in FreeBSD 6.1 in libthr
|
|
* and uses atomic operations and umtx syscall. However, until FreeBSD 9.0
|
|
* a choice of implementation depended on linking order of libthr and libc.
|
|
* In FreeBSD 9.0 the umtx implementation has been moved to libc.
|
|
*
|
|
* Solaris have POSIX semaphores.
|
|
*
|
|
* MacOSX has limited POSIX semaphore implementation:
|
|
* 1) sem_init() exists but returns ENOSYS;
|
|
* 2) no sem_timedwait().
|
|
*/
|
|
|
|
nxt_int_t
|
|
nxt_sem_init(nxt_sem_t *sem, nxt_uint_t count)
|
|
{
|
|
if (sem_init(sem, 0, count) == 0) {
|
|
nxt_thread_log_debug("sem_init(%p)", sem);
|
|
return NXT_OK;
|
|
}
|
|
|
|
nxt_thread_log_alert("sem_init(%p) failed %E", sem, nxt_errno);
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
|
|
void
|
|
nxt_sem_destroy(nxt_sem_t *sem)
|
|
{
|
|
if (sem_destroy(sem) == 0) {
|
|
nxt_thread_log_debug("sem_destroy(%p)", sem);
|
|
return;
|
|
}
|
|
|
|
nxt_thread_log_alert("sem_destroy(%p) failed %E", sem, nxt_errno);
|
|
}
|
|
|
|
|
|
nxt_int_t
|
|
nxt_sem_post(nxt_sem_t *sem)
|
|
{
|
|
nxt_thread_log_debug("sem_post(%p)", sem);
|
|
|
|
if (nxt_fast_path(sem_post(sem) == 0)) {
|
|
return NXT_OK;
|
|
}
|
|
|
|
nxt_thread_log_alert("sem_post(%p) failed %E", sem, nxt_errno);
|
|
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
|
|
nxt_err_t
|
|
nxt_sem_wait(nxt_sem_t *sem, nxt_nsec_t timeout)
|
|
{
|
|
int n;
|
|
nxt_err_t err;
|
|
nxt_nsec_t ns;
|
|
nxt_thread_t *thr;
|
|
nxt_realtime_t *now;
|
|
struct timespec ts;
|
|
|
|
thr = nxt_thread();
|
|
|
|
if (timeout == NXT_INFINITE_NSEC) {
|
|
nxt_log_debug(thr->log, "sem_wait(%p) enter", sem);
|
|
|
|
for ( ;; ) {
|
|
n = sem_wait(sem);
|
|
|
|
err = nxt_errno;
|
|
|
|
nxt_thread_time_update(thr);
|
|
|
|
if (nxt_fast_path(n == 0)) {
|
|
nxt_thread_log_debug("sem_wait(%p) exit", sem);
|
|
return 0;
|
|
}
|
|
|
|
switch (err) {
|
|
|
|
case NXT_EINTR:
|
|
nxt_log_error(NXT_LOG_INFO, thr->log, "sem_wait(%p) failed %E",
|
|
sem, err);
|
|
continue;
|
|
|
|
default:
|
|
nxt_log_alert(thr->log, "sem_wait(%p) failed %E", sem, err);
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if (NXT_HAVE_SEM_TRYWAIT_FAST)
|
|
|
|
nxt_log_debug(thr->log, "sem_trywait(%p) enter", sem);
|
|
|
|
/*
|
|
* Fast sem_trywait() using atomic operations may eliminate
|
|
* timeout processing.
|
|
*/
|
|
|
|
if (nxt_fast_path(sem_trywait(sem) == 0)) {
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
nxt_log_debug(thr->log, "sem_timedwait(%p, %N) enter", sem, timeout);
|
|
|
|
now = nxt_thread_realtime(thr);
|
|
ns = now->nsec + timeout;
|
|
ts.tv_sec = now->sec + ns / 1000000000;
|
|
ts.tv_nsec = ns % 1000000000;
|
|
|
|
for ( ;; ) {
|
|
n = sem_timedwait(sem, &ts);
|
|
|
|
err = nxt_errno;
|
|
|
|
nxt_thread_time_update(thr);
|
|
|
|
if (nxt_fast_path(n == 0)) {
|
|
nxt_thread_log_debug("sem_timedwait(%p) exit", sem);
|
|
return 0;
|
|
}
|
|
|
|
switch (err) {
|
|
|
|
case NXT_ETIMEDOUT:
|
|
nxt_log_debug(thr->log, "sem_timedwait(%p) exit: %d", sem, err);
|
|
return err;
|
|
|
|
case NXT_EINTR:
|
|
nxt_log_error(NXT_LOG_INFO, thr->log, "sem_timedwait(%p) failed %E",
|
|
sem, err);
|
|
continue;
|
|
|
|
default:
|
|
nxt_log_alert(thr->log, "sem_timedwait(%p) failed %E", sem, err);
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
/* Semaphore implementation using pthread conditional variable. */
|
|
|
|
nxt_int_t
|
|
nxt_sem_init(nxt_sem_t *sem, nxt_uint_t count)
|
|
{
|
|
if (nxt_thread_mutex_create(&sem->mutex) == NXT_OK) {
|
|
|
|
if (nxt_thread_cond_create(&sem->cond) == NXT_OK) {
|
|
sem->count = count;
|
|
return NXT_OK;
|
|
}
|
|
|
|
nxt_thread_mutex_destroy(&sem->mutex);
|
|
}
|
|
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
|
|
void
|
|
nxt_sem_destroy(nxt_sem_t *sem)
|
|
{
|
|
nxt_thread_cond_destroy(&sem->cond);
|
|
nxt_thread_mutex_destroy(&sem->mutex);
|
|
}
|
|
|
|
|
|
nxt_int_t
|
|
nxt_sem_post(nxt_sem_t *sem)
|
|
{
|
|
nxt_int_t ret;
|
|
|
|
if (nxt_slow_path(nxt_thread_mutex_lock(&sem->mutex) != NXT_OK)) {
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
ret = nxt_thread_cond_signal(&sem->cond);
|
|
|
|
sem->count++;
|
|
|
|
/* NXT_ERROR overrides NXT_OK. */
|
|
|
|
return (nxt_thread_mutex_unlock(&sem->mutex) | ret);
|
|
}
|
|
|
|
|
|
nxt_err_t
|
|
nxt_sem_wait(nxt_sem_t *sem, nxt_nsec_t timeout)
|
|
{
|
|
nxt_err_t err;
|
|
|
|
err = 0;
|
|
|
|
if (nxt_slow_path(nxt_thread_mutex_lock(&sem->mutex) != NXT_OK)) {
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
while (sem->count == 0) {
|
|
|
|
err = nxt_thread_cond_wait(&sem->cond, &sem->mutex, timeout);
|
|
|
|
if (err != 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
sem->count--;
|
|
|
|
error:
|
|
|
|
/* NXT_ERROR overrides NXT_OK and NXT_ETIMEDOUT. */
|
|
|
|
return (nxt_thread_mutex_unlock(&sem->mutex) | err);
|
|
}
|
|
|
|
#endif
|