|
Packit |
5e354d |
/* npth.c - a lightweight implementation of pth over pthread.
|
|
Packit |
5e354d |
* Copyright (C) 2011 g10 Code GmbH
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* This file is part of nPth.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* nPth is free software; you can redistribute it and/or modify
|
|
Packit |
5e354d |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
5e354d |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
5e354d |
* the License, or (at your option) any later version.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* nPth is distributed in the hope that it will be useful, but
|
|
Packit |
5e354d |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e354d |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
Packit |
5e354d |
* the GNU Lesser General Public License for more details.
|
|
Packit |
5e354d |
*
|
|
Packit |
5e354d |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e354d |
* License along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
Packit |
5e354d |
*/
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_CONFIG_H
|
|
Packit |
5e354d |
#include <config.h>
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#include <stdlib.h>
|
|
Packit |
5e354d |
#include <stdio.h>
|
|
Packit |
5e354d |
#include <string.h>
|
|
Packit |
5e354d |
#include <assert.h>
|
|
Packit |
5e354d |
#include <errno.h>
|
|
Packit |
5e354d |
#include <pthread.h>
|
|
Packit |
5e354d |
#include <fcntl.h>
|
|
Packit |
5e354d |
#include <sys/stat.h>
|
|
Packit |
5e354d |
#ifdef HAVE_LIB_DISPATCH
|
|
Packit |
5e354d |
# include <dispatch/dispatch.h>
|
|
Packit |
5e354d |
typedef dispatch_semaphore_t sem_t;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* This glue code is for macOS which does not have full implementation
|
|
Packit |
5e354d |
of POSIX semaphore. On macOS, using semaphore in Grand Central
|
|
Packit |
5e354d |
Dispatch library is better than using the partial implementation of
|
|
Packit |
5e354d |
POSIX semaphore where sem_init doesn't work well.
|
|
Packit |
5e354d |
*/
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
sem_init (sem_t *sem, int is_shared, unsigned int value)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
(void)is_shared;
|
|
Packit |
5e354d |
if ((*sem = dispatch_semaphore_create (value)) == NULL)
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
sem_post (sem_t *sem)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
dispatch_semaphore_signal (*sem);
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
sem_wait (sem_t *sem)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
dispatch_semaphore_wait (*sem, DISPATCH_TIME_FOREVER);
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
# include <semaphore.h>
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
#ifdef HAVE_UNISTD_H
|
|
Packit |
5e354d |
# include <unistd.h>
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
#ifndef HAVE_PSELECT
|
|
Packit |
5e354d |
# include <signal.h>
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#include "npth.h"
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The global lock that excludes all threads but one. This is a
|
|
Packit |
5e354d |
semaphore, because these can be safely used in a library even if
|
|
Packit |
5e354d |
the application or other libraries call fork(), including from a
|
|
Packit |
5e354d |
signal handler. sem_post is async-signal-safe. (The reason a
|
|
Packit |
5e354d |
semaphore is safe and a mutex is not safe is that a mutex has an
|
|
Packit |
5e354d |
owner, while a semaphore does not.) We init sceptre to a static
|
|
Packit |
5e354d |
buffer for use by sem_init; in case sem_open is used instead
|
|
Packit |
5e354d |
SCEPTRE will changed to the value returned by sem_open.
|
|
Packit |
5e354d |
GOT_SCEPTRE is a flag used for debugging to tell wether we hold
|
|
Packit |
5e354d |
SCEPTRE. */
|
|
Packit |
5e354d |
static sem_t sceptre_buffer;
|
|
Packit |
5e354d |
static sem_t *sceptre = &sceptre_buffer;
|
|
Packit |
5e354d |
static int got_sceptre;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Configure defines HAVE_FORK_UNSAFE_SEMAPHORE if child process can't
|
|
Packit |
5e354d |
access non-shared unnamed semaphore which is created by its parent.
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
We use unnamed semaphore (if available) for the global lock. The
|
|
Packit |
5e354d |
specific semaphore is only valid for those threads in a process,
|
|
Packit |
5e354d |
and it is no use by other processes. Thus, PSHARED argument for
|
|
Packit |
5e354d |
sem_init is naturally 0.
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
However, there are daemon-like applications which use fork after
|
|
Packit |
5e354d |
npth's initialization by npth_init. In this case, a child process
|
|
Packit |
5e354d |
uses the semaphore which was created by its parent process, while
|
|
Packit |
5e354d |
parent does nothing with the semaphore. In some system (e.g. AIX),
|
|
Packit |
5e354d |
access by child process to non-shared unnamed semaphore is
|
|
Packit |
5e354d |
prohibited. For such a system, HAVE_FORK_UNSAFE_SEMAPHORE should
|
|
Packit |
5e354d |
be defined, so that unnamed semaphore will be created with the
|
|
Packit |
5e354d |
option PSHARED=1. The purpose of the setting of PSHARED=1 is only
|
|
Packit |
5e354d |
for allowing the access of the lock by child process. For NPTH, it
|
|
Packit |
5e354d |
does not mean any other interactions between processes.
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
*/
|
|
Packit |
5e354d |
#ifdef HAVE_FORK_UNSAFE_SEMAPHORE
|
|
Packit |
5e354d |
#define NPTH_SEMAPHORE_PSHARED 1
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
#define NPTH_SEMAPHORE_PSHARED 0
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The main thread is the active thread at the time pth_init was
|
|
Packit |
5e354d |
called. As of now it is only useful for debugging. The volatile
|
|
Packit |
5e354d |
make sure the compiler does not eliminate this set but not used
|
|
Packit |
5e354d |
variable. */
|
|
Packit |
5e354d |
static volatile pthread_t main_thread;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* This flag is set as soon as npth_init has been called or if any
|
|
Packit |
5e354d |
* thread has been created. It will never be cleared again. The only
|
|
Packit |
5e354d |
* purpose is to make npth_protect and npth_unprotect more robust in
|
|
Packit |
5e354d |
* that they can be shortcut when npth_init has not yet been called.
|
|
Packit |
5e354d |
* This is important for libraries which want to support nPth by using
|
|
Packit |
5e354d |
* those two functions but may have be initialized before pPth. */
|
|
Packit |
5e354d |
static int initialized_or_any_threads;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Systems that don't have pthread_mutex_timedlock get a busy wait
|
|
Packit |
5e354d |
implementation that probes the lock every BUSY_WAIT_INTERVAL
|
|
Packit |
5e354d |
milliseconds. */
|
|
Packit |
5e354d |
#define BUSY_WAIT_INTERVAL 200
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
typedef int (*trylock_func_t) (void *);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static int
|
|
Packit |
5e354d |
busy_wait_for (trylock_func_t trylock, void *lock,
|
|
Packit |
5e354d |
const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* This is not great, but better than nothing. Only works for locks
|
|
Packit |
5e354d |
which are mostly uncontested. Provides absolutely no fairness at
|
|
Packit |
5e354d |
all. Creates many wake-ups. */
|
|
Packit |
5e354d |
while (1)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
struct timespec ts;
|
|
Packit |
5e354d |
err = npth_clock_gettime (&ts);
|
|
Packit |
5e354d |
if (err < 0)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* Just for safety make sure we return some error. */
|
|
Packit |
5e354d |
err = errno ? errno : EINVAL;
|
|
Packit |
5e354d |
break;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (npth_timercmp (abstime, &ts, <))
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
err = ETIMEDOUT;
|
|
Packit |
5e354d |
break;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
err = (*trylock) (lock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
break;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Try again after waiting a bit. We could calculate the
|
|
Packit |
5e354d |
maximum wait time from ts and abstime, but we don't
|
|
Packit |
5e354d |
bother, as our granularity is pretty fine. */
|
|
Packit |
5e354d |
usleep (BUSY_WAIT_INTERVAL * 1000);
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
enter_npth (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
got_sceptre = 0;
|
|
Packit |
5e354d |
res = sem_post (sceptre);
|
|
Packit |
5e354d |
assert (res == 0);
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void
|
|
Packit |
5e354d |
leave_npth (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
int save_errno = errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
do {
|
|
Packit |
5e354d |
res = sem_wait (sceptre);
|
|
Packit |
5e354d |
} while (res < 0 && errno == EINTR);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
assert (!res);
|
|
Packit |
5e354d |
got_sceptre = 1;
|
|
Packit |
5e354d |
errno = save_errno;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#define ENTER() enter_npth ()
|
|
Packit |
5e354d |
#define LEAVE() leave_npth ()
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_init (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
main_thread = pthread_self();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Track that we have been initialized. */
|
|
Packit |
5e354d |
initialized_or_any_threads |= 1;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Better reset ERRNO so that we know that it has been set by
|
|
Packit |
5e354d |
sem_init. */
|
|
Packit |
5e354d |
errno = 0;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* The semaphore is binary. */
|
|
Packit |
5e354d |
res = sem_init (sceptre, NPTH_SEMAPHORE_PSHARED, 1);
|
|
Packit |
5e354d |
/* There are some versions of operating systems which have sem_init
|
|
Packit |
5e354d |
symbol defined but the call actually returns ENOSYS at runtime.
|
|
Packit |
5e354d |
We know this problem for older versions of AIX (<= 4.3.3) and
|
|
Packit |
5e354d |
macOS. For macOS, we use semaphore in Grand Central Dispatch
|
|
Packit |
5e354d |
library, so ENOSYS doesn't happen. We only support AIX >= 5.2,
|
|
Packit |
5e354d |
where sem_init is supported.
|
|
Packit |
5e354d |
*/
|
|
Packit |
5e354d |
if (res < 0)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* POSIX.1-2001 defines the semaphore interface but does not
|
|
Packit |
5e354d |
specify the return value for success. Thus we better
|
|
Packit |
5e354d |
bail out on error only on a POSIX.1-2008 system. */
|
|
Packit |
5e354d |
#if _POSIX_C_SOURCE >= 200809L
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_getname_np (npth_t target_thread, char *buf, size_t buflen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_GETNAME_NP
|
|
Packit |
5e354d |
return pthread_getname_np (target_thread, buf, buflen);
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
(void)target_thread;
|
|
Packit |
5e354d |
(void)buf;
|
|
Packit |
5e354d |
(void)buflen;
|
|
Packit |
5e354d |
return ENOSYS;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_setname_np (npth_t target_thread, const char *name)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_SETNAME_NP
|
|
Packit |
5e354d |
#ifdef __NetBSD__
|
|
Packit |
5e354d |
return pthread_setname_np (target_thread, "%s", (void*) name);
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
#ifdef __APPLE__
|
|
Packit |
5e354d |
if (target_thread == npth_self ())
|
|
Packit |
5e354d |
return pthread_setname_np (name);
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
return ENOTSUP;
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
return pthread_setname_np (target_thread, name);
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
(void)target_thread;
|
|
Packit |
5e354d |
(void)name;
|
|
Packit |
5e354d |
return ENOSYS;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct startup_s
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
void *(*start_routine) (void *);
|
|
Packit |
5e354d |
void *arg;
|
|
Packit |
5e354d |
};
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
static void *
|
|
Packit |
5e354d |
thread_start (void *startup_arg)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
struct startup_s *startup = startup_arg;
|
|
Packit |
5e354d |
void *(*start_routine) (void *);
|
|
Packit |
5e354d |
void *arg;
|
|
Packit |
5e354d |
void *result;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
start_routine = startup->start_routine;
|
|
Packit |
5e354d |
arg = startup->arg;
|
|
Packit |
5e354d |
free (startup);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
result = (*start_routine) (arg);
|
|
Packit |
5e354d |
/* Note: instead of returning here, we might end up in
|
|
Packit |
5e354d |
npth_exit() instead. */
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
return result;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_create (npth_t *thread, const npth_attr_t *attr,
|
|
Packit |
5e354d |
void *(*start_routine) (void *), void *arg)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
struct startup_s *startup;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
startup = malloc (sizeof (*startup));
|
|
Packit |
5e354d |
if (!startup)
|
|
Packit |
5e354d |
return errno;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
initialized_or_any_threads |= 2;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
startup->start_routine = start_routine;
|
|
Packit |
5e354d |
startup->arg = arg;
|
|
Packit |
5e354d |
err = pthread_create (thread, attr, thread_start, startup);
|
|
Packit |
5e354d |
if (err)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
free (startup);
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Memory is released in thread_start. */
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_join (npth_t thread, void **retval)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_TRYJOIN_NP
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. pthread_tryjoin_np is a GNU extension. */
|
|
Packit |
5e354d |
err = pthread_tryjoin_np (thread, retval);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
#endif /*HAVE_PTHREAD_TRYJOIN_NP*/
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_join (thread, retval);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_exit (void *retval)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
pthread_exit (retval);
|
|
Packit |
5e354d |
/* Never reached. But just in case pthread_exit does return... */
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_lock (npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_mutex_trylock (mutex);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_mutex_lock (mutex);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_mutex_timedlock (npth_mutex_t *mutex, const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_mutex_trylock (mutex);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
#if HAVE_PTHREAD_MUTEX_TIMEDLOCK
|
|
Packit |
5e354d |
err = pthread_mutex_timedlock (mutex, abstime);
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
err = busy_wait_for ((trylock_func_t) pthread_mutex_trylock, mutex, abstime);
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifndef _NPTH_NO_RWLOCK
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_rdlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_rwlock_tryrdlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_rwlock_rdlock (rwlock);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_timedrdlock (npth_rwlock_t *rwlock, const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_rwlock_tryrdlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
#if HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK
|
|
Packit |
5e354d |
err = pthread_rwlock_timedrdlock (rwlock, abstime);
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
err = busy_wait_for ((trylock_func_t) pthread_rwlock_tryrdlock, rwlock,
|
|
Packit |
5e354d |
abstime);
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_wrlock (npth_rwlock_t *rwlock)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_rwlock_trywrlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_rwlock_wrlock (rwlock);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_rwlock_timedwrlock (npth_rwlock_t *rwlock, const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
#ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK
|
|
Packit |
5e354d |
/* No need to allow competing threads to enter when we can get the
|
|
Packit |
5e354d |
lock immediately. */
|
|
Packit |
5e354d |
err = pthread_rwlock_trywrlock (rwlock);
|
|
Packit |
5e354d |
if (err != EBUSY)
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
#if HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK
|
|
Packit |
5e354d |
err = pthread_rwlock_timedwrlock (rwlock, abstime);
|
|
Packit |
5e354d |
#elif HAVE_PTHREAD_RWLOCK_TRYRDLOCK
|
|
Packit |
5e354d |
err = busy_wait_for ((trylock_func_t) pthread_rwlock_trywrlock, rwlock,
|
|
Packit |
5e354d |
abstime);
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
err = ENOSYS;
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_cond_wait (cond, mutex);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex,
|
|
Packit |
5e354d |
const struct timespec *abstime)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int err;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
err = pthread_cond_timedwait (cond, mutex, abstime);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return err;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
/* Standard POSIX Replacement API */
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_usleep(unsigned int usec)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = usleep(usec);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
unsigned int
|
|
Packit |
5e354d |
npth_sleep(unsigned int sec)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
unsigned res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = sleep(sec);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_system(const char *cmd)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = system(cmd);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
pid_t
|
|
Packit |
5e354d |
npth_waitpid(pid_t pid, int *status, int options)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
pid_t res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = waitpid(pid,status, options);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_connect(int s, const struct sockaddr *addr, socklen_t addrlen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = connect(s, addr, addrlen);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = accept(s, addr, addrlen);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_select(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
|
Packit |
5e354d |
struct timeval *timeout)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = select(nfd, rfds, wfds, efds, timeout);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_pselect(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
|
Packit |
5e354d |
const struct timespec *timeout, const sigset_t *sigmask)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
#ifdef HAVE_PSELECT
|
|
Packit |
5e354d |
res = pselect (nfd, rfds, wfds, efds, timeout, sigmask);
|
|
Packit |
5e354d |
#else /*!HAVE_PSELECT*/
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* A better emulation of pselect would be to create a pipe, wait
|
|
Packit |
5e354d |
in the select for one end and have a signal handler write to
|
|
Packit |
5e354d |
the other end. However, this is non-trivial to implement and
|
|
Packit |
5e354d |
thus we only print a compile time warning. */
|
|
Packit |
5e354d |
# ifdef __GNUC__
|
|
Packit |
5e354d |
# warning Using a non race free pselect emulation.
|
|
Packit |
5e354d |
# endif
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
struct timeval t, *tp;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
tp = NULL;
|
|
Packit |
5e354d |
if (!timeout)
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
else if (timeout->tv_nsec >= 0 && timeout->tv_nsec < 1000000000)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
t.tv_sec = timeout->tv_sec;
|
|
Packit |
5e354d |
t.tv_usec = (timeout->tv_nsec + 999) / 1000;
|
|
Packit |
5e354d |
tp = &t;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
errno = EINVAL;
|
|
Packit |
5e354d |
res = -1;
|
|
Packit |
5e354d |
goto leave;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (sigmask)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int save_errno;
|
|
Packit |
5e354d |
sigset_t savemask;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
pthread_sigmask (SIG_SETMASK, sigmask, &savemask);
|
|
Packit |
5e354d |
res = select (nfd, rfds, wfds, efds, tp);
|
|
Packit |
5e354d |
save_errno = errno;
|
|
Packit |
5e354d |
pthread_sigmask (SIG_SETMASK, &savemask, NULL);
|
|
Packit |
5e354d |
errno = save_errno;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
else
|
|
Packit |
5e354d |
res = select (nfd, rfds, wfds, efds, tp);
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
leave:
|
|
Packit |
5e354d |
;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
#endif /*!HAVE_PSELECT*/
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ssize_t
|
|
Packit |
5e354d |
npth_read(int fd, void *buf, size_t nbytes)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ssize_t res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = read(fd, buf, nbytes);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ssize_t
|
|
Packit |
5e354d |
npth_write(int fd, const void *buf, size_t nbytes)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
ssize_t res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = write(fd, buf, nbytes);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_recvmsg (int fd, struct msghdr *msg, int flags)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = recvmsg (fd, msg, flags);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_sendmsg (int fd, const struct msghdr *msg, int flags)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
int res;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
res = sendmsg (fd, msg, flags);
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
return res;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_unprotect (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* If we are not initialized we may not access the semaphore and
|
|
Packit |
5e354d |
* thus we shortcut it. Note that in this case the unprotect/protect
|
|
Packit |
5e354d |
* is not needed. For failsafe reasons if an nPth thread has ever
|
|
Packit |
5e354d |
* been created but nPth has accidentally not initialized we do not
|
|
Packit |
5e354d |
* shortcut so that a stack backtrace (due to the access of the
|
|
Packit |
5e354d |
* uninitialized semaphore) is more expressive. */
|
|
Packit |
5e354d |
if (initialized_or_any_threads)
|
|
Packit |
5e354d |
ENTER();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
void
|
|
Packit |
5e354d |
npth_protect (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
/* See npth_unprotect for commentary. */
|
|
Packit |
5e354d |
if (initialized_or_any_threads)
|
|
Packit |
5e354d |
LEAVE();
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_is_protected (void)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
return got_sceptre;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
int
|
|
Packit |
5e354d |
npth_clock_gettime (struct timespec *ts)
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
#if defined(CLOCK_REALTIME) && HAVE_CLOCK_GETTIME
|
|
Packit |
5e354d |
return clock_gettime (CLOCK_REALTIME, ts);
|
|
Packit |
5e354d |
#elif HAVE_GETTIMEOFDAY
|
|
Packit |
5e354d |
{
|
|
Packit |
5e354d |
struct timeval tv;
|
|
Packit |
5e354d |
|
|
Packit |
5e354d |
if (gettimeofday (&tv, NULL))
|
|
Packit |
5e354d |
return -1;
|
|
Packit |
5e354d |
ts->tv_sec = tv.tv_sec;
|
|
Packit |
5e354d |
ts->tv_nsec = tv.tv_usec * 1000;
|
|
Packit |
5e354d |
return 0;
|
|
Packit |
5e354d |
}
|
|
Packit |
5e354d |
#else
|
|
Packit |
5e354d |
/* FIXME: fall back on time() with seconds resolution. */
|
|
Packit |
5e354d |
# error clock_gettime not available - please provide a fallback.
|
|
Packit |
5e354d |
#endif
|
|
Packit |
5e354d |
}
|