cosmopolitan/third_party/python/Python/thread_pthread.inc
Justine Tunney 9b29358511 Make whitespace changes
Status lines for Emacs and Vim have been added to Python sources so
they'll be easier to edit using Python's preferred coding style.

Some DNS helper functions have been broken up into multiple files. It's
nice to have one function per file whenever possible, since that way we
don't need -ffunction-sections.  Another reason it's good to have small
source files, is because the build will be enforcing resource limits on
compilation and testing soon.
2021-08-13 03:20:45 -07:00

661 lines
18 KiB
C++

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Python 3 │
│ https://docs.python.org/3/license.html │
╚─────────────────────────────────────────────────────────────────────────────*/
/* clang-format off */
/* Posix threads interface */
/* The POSIX spec requires that use of pthread_attr_setstacksize
be conditional on _POSIX_THREAD_ATTR_STACKSIZE being defined. */
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE 0 /* use default stack size */
#endif
/* The default stack size for new threads on OSX and BSD is small enough that
* we'll get hard crashes instead of 'maximum recursion depth exceeded'
* exceptions.
*
* The default stack sizes below are the empirically determined minimal stack
* sizes where a simple recursive function doesn't cause a hard crash.
*/
#if defined(__APPLE__) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
#undef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE 0x500000
#endif
#if defined(__FreeBSD__) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
#undef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE 0x400000
#endif
/* for safety, ensure a viable minimum stacksize */
#define THREAD_STACK_MIN 0x8000 /* 32kB */
#else /* !_POSIX_THREAD_ATTR_STACKSIZE */
#ifdef THREAD_STACK_SIZE
#error "THREAD_STACK_SIZE defined but _POSIX_THREAD_ATTR_STACKSIZE undefined"
#endif
#endif
#if !defined(pthread_attr_default)
# define pthread_attr_default ((pthread_attr_t *)NULL)
#endif
#if !defined(pthread_mutexattr_default)
# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL)
#endif
#if !defined(pthread_condattr_default)
# define pthread_condattr_default ((pthread_condattr_t *)NULL)
#endif
/* Whether or not to use semaphores directly rather than emulating them with
* mutexes and condition variables:
*/
#if (defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES) && \
defined(HAVE_SEM_TIMEDWAIT))
# define USE_SEMAPHORES
#else
# undef USE_SEMAPHORES
#endif
/* On platforms that don't use standard POSIX threads pthread_sigmask()
* isn't present. DEC threads uses sigprocmask() instead as do most
* other UNIX International compliant systems that don't have the full
* pthread implementation.
*/
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
# define SET_THREAD_SIGMASK pthread_sigmask
#else
# define SET_THREAD_SIGMASK sigprocmask
#endif
/* We assume all modern POSIX systems have gettimeofday() */
#ifdef GETTIMEOFDAY_NO_TZ
#define GETTIMEOFDAY(ptv) gettimeofday(ptv)
#else
#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
#endif
#define MICROSECONDS_TO_TIMESPEC(microseconds, ts) \
do { \
struct timeval tv; \
GETTIMEOFDAY(&tv); \
tv.tv_usec += microseconds % 1000000; \
tv.tv_sec += microseconds / 1000000; \
tv.tv_sec += tv.tv_usec / 1000000; \
tv.tv_usec %= 1000000; \
ts.tv_sec = tv.tv_sec; \
ts.tv_nsec = tv.tv_usec * 1000; \
} while(0)
/* A pthread mutex isn't sufficient to model the Python lock type
* because, according to Draft 5 of the docs (P1003.4a/D5), both of the
* following are undefined:
* -> a thread tries to lock a mutex it already has locked
* -> a thread tries to unlock a mutex locked by a different thread
* pthread mutexes are designed for serializing threads over short pieces
* of code anyway, so wouldn't be an appropriate implementation of
* Python's locks regardless.
*
* The pthread_lock struct implements a Python lock as a "locked?" bit
* and a <condition, mutex> pair. In general, if the bit can be acquired
* instantly, it is, else the pair is used to block the thread until the
* bit is cleared. 9 May 1994 tim@ksr.com
*/
typedef struct {
char locked; /* 0=unlocked, 1=locked */
/* a <cond, mutex> pair to handle an acquire of a locked lock */
pthread_cond_t lock_released;
pthread_mutex_t mut;
} pthread_lock;
#define CHECK_STATUS(name) if (status != 0) { perror(name); error = 1; }
#define CHECK_STATUS_PTHREAD(name) if (status != 0) { fprintf(stderr, \
"%s: %s\n", name, strerror(status)); error = 1; }
/*
* Initialization.
*/
#if defined(_HAVE_BSDI)
static
void _noop(void)
{
}
static void
PyThread__init_thread(void)
{
/* DO AN INIT BY STARTING THE THREAD */
static int dummy = 0;
pthread_t thread1;
pthread_create(&thread1, NULL, (void *) _noop, &dummy);
pthread_join(thread1, NULL);
}
#else /* !_HAVE_BSDI */
static void
PyThread__init_thread(void)
{
#if defined(_AIX) && defined(__GNUC__)
extern void pthread_init(void);
pthread_init();
#endif
}
#endif /* !_HAVE_BSDI */
/*
* Thread support.
*/
/* bpo-33015: pythread_callback struct and pythread_wrapper() cast
"void func(void *)" to "void* func(void *)": always return NULL.
PyThread_start_new_thread() uses "void func(void *)" type, whereas
pthread_create() requires a void* return value. */
typedef struct {
void (*func) (void *);
void *arg;
} pythread_callback;
static void *
pythread_wrapper(void *arg)
{
/* copy func and func_arg and free the temporary structure */
pythread_callback *callback = arg;
void (*func)(void *) = callback->func;
void *func_arg = callback->arg;
PyMem_RawFree(arg);
func(func_arg);
return NULL;
}
long
PyThread_start_new_thread(void (*func)(void *), void *arg)
{
pthread_t th;
int status;
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
pthread_attr_t attrs;
#endif
#if defined(THREAD_STACK_SIZE)
size_t tss;
#endif
dprintf(("PyThread_start_new_thread called\n"));
if (!initialized)
PyThread_init_thread();
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
if (pthread_attr_init(&attrs) != 0)
return -1;
#endif
#if defined(THREAD_STACK_SIZE)
tss = (_pythread_stacksize != 0) ? _pythread_stacksize
: THREAD_STACK_SIZE;
if (tss != 0) {
if (pthread_attr_setstacksize(&attrs, tss) != 0) {
pthread_attr_destroy(&attrs);
return -1;
}
}
#endif
#if defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM);
#endif
pythread_callback *callback = PyMem_RawMalloc(sizeof(pythread_callback));
if (callback == NULL) {
return -1;
}
callback->func = func;
callback->arg = arg;
status = pthread_create(&th,
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
&attrs,
#else
(pthread_attr_t*)NULL,
#endif
pythread_wrapper, callback);
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
pthread_attr_destroy(&attrs);
#endif
if (status != 0) {
PyMem_RawFree(callback);
return -1;
}
pthread_detach(th);
#if SIZEOF_PTHREAD_T <= SIZEOF_LONG
return (long) th;
#else
return (long) *(long *) &th;
#endif
}
/* XXX This implementation is considered (to quote Tim Peters) "inherently
hosed" because:
- It does not guarantee the promise that a non-zero integer is returned.
- The cast to long is inherently unsafe.
- It is not clear that the 'volatile' (for AIX?) are any longer necessary.
*/
long
PyThread_get_thread_ident(void)
{
volatile pthread_t threadid;
if (!initialized)
PyThread_init_thread();
threadid = pthread_self();
return (long) threadid;
}
void
PyThread_exit_thread(void)
{
dprintf(("PyThread_exit_thread called\n"));
if (!initialized)
exit(0);
pthread_exit(0);
}
#ifdef USE_SEMAPHORES
/*
* Lock support.
*/
PyThread_type_lock
PyThread_allocate_lock(void)
{
sem_t *lock;
int status, error = 0;
dprintf(("PyThread_allocate_lock called\n"));
if (!initialized)
PyThread_init_thread();
lock = (sem_t *)PyMem_RawMalloc(sizeof(sem_t));
if (lock) {
status = sem_init(lock,0,1);
CHECK_STATUS("sem_init");
if (error) {
PyMem_RawFree((void *)lock);
lock = NULL;
}
}
dprintf(("PyThread_allocate_lock() -> %p\n", lock));
return (PyThread_type_lock)lock;
}
void
PyThread_free_lock(PyThread_type_lock lock)
{
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_free_lock(%p) called\n", lock));
if (!thelock)
return;
status = sem_destroy(thelock);
CHECK_STATUS("sem_destroy");
PyMem_RawFree((void *)thelock);
}
/*
* As of February 2002, Cygwin thread implementations mistakenly report error
* codes in the return value of the sem_ calls (like the pthread_ functions).
* Correct implementations return -1 and put the code in errno. This supports
* either.
*/
static int
fix_status(int status)
{
return (status == -1) ? errno : status;
}
PyLockStatus
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
int intr_flag)
{
PyLockStatus success;
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
struct timespec ts;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
lock, microseconds, intr_flag));
if (microseconds > 0)
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
do {
if (microseconds > 0)
status = fix_status(sem_timedwait(thelock, &ts));
else if (microseconds == 0)
status = fix_status(sem_trywait(thelock));
else
status = fix_status(sem_wait(thelock));
/* Retry if interrupted by a signal, unless the caller wants to be
notified. */
} while (!intr_flag && status == EINTR);
/* Don't check the status if we're stopping because of an interrupt. */
if (!(intr_flag && status == EINTR)) {
if (microseconds > 0) {
if (status != ETIMEDOUT)
CHECK_STATUS("sem_timedwait");
}
else if (microseconds == 0) {
if (status != EAGAIN)
CHECK_STATUS("sem_trywait");
}
else {
CHECK_STATUS("sem_wait");
}
}
if (status == 0) {
success = PY_LOCK_ACQUIRED;
} else if (intr_flag && status == EINTR) {
success = PY_LOCK_INTR;
} else {
success = PY_LOCK_FAILURE;
}
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",
lock, microseconds, intr_flag, success));
return success;
}
void
PyThread_release_lock(PyThread_type_lock lock)
{
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_release_lock(%p) called\n", lock));
status = sem_post(thelock);
CHECK_STATUS("sem_post");
}
#else /* USE_SEMAPHORES */
/*
* Lock support.
*/
PyThread_type_lock
PyThread_allocate_lock(void)
{
pthread_lock *lock;
int status, error = 0;
dprintf(("PyThread_allocate_lock called\n"));
if (!initialized)
PyThread_init_thread();
lock = (pthread_lock *) PyMem_RawMalloc(sizeof(pthread_lock));
if (lock) {
memset((void *)lock, '\0', sizeof(pthread_lock));
lock->locked = 0;
status = pthread_mutex_init(&lock->mut,
pthread_mutexattr_default);
CHECK_STATUS_PTHREAD("pthread_mutex_init");
/* Mark the pthread mutex underlying a Python mutex as
pure happens-before. We can't simply mark the
Python-level mutex as a mutex because it can be
acquired and released in different threads, which
will cause errors. */
_Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut);
status = pthread_cond_init(&lock->lock_released,
pthread_condattr_default);
CHECK_STATUS_PTHREAD("pthread_cond_init");
if (error) {
PyMem_RawFree((void *)lock);
lock = 0;
}
}
dprintf(("PyThread_allocate_lock() -> %p\n", lock));
return (PyThread_type_lock) lock;
}
void
PyThread_free_lock(PyThread_type_lock lock)
{
pthread_lock *thelock = (pthread_lock *)lock;
int status, error = 0;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_free_lock(%p) called\n", lock));
/* some pthread-like implementations tie the mutex to the cond
* and must have the cond destroyed first.
*/
status = pthread_cond_destroy( &thelock->lock_released );
CHECK_STATUS_PTHREAD("pthread_cond_destroy");
status = pthread_mutex_destroy( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_destroy");
PyMem_RawFree((void *)thelock);
}
PyLockStatus
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
int intr_flag)
{
PyLockStatus success = PY_LOCK_FAILURE;
pthread_lock *thelock = (pthread_lock *)lock;
int status, error = 0;
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
lock, microseconds, intr_flag));
if (microseconds == 0) {
status = pthread_mutex_trylock( &thelock->mut );
if (status != EBUSY)
CHECK_STATUS_PTHREAD("pthread_mutex_trylock[1]");
}
else {
status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
}
if (status == 0) {
if (thelock->locked == 0) {
success = PY_LOCK_ACQUIRED;
}
else if (microseconds != 0) {
struct timespec ts;
if (microseconds > 0)
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
/* continue trying until we get the lock */
/* mut must be locked by me -- part of the condition
* protocol */
while (success == PY_LOCK_FAILURE) {
if (microseconds > 0) {
status = pthread_cond_timedwait(
&thelock->lock_released,
&thelock->mut, &ts);
if (status == ETIMEDOUT)
break;
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
}
else {
status = pthread_cond_wait(
&thelock->lock_released,
&thelock->mut);
CHECK_STATUS_PTHREAD("pthread_cond_wait");
}
if (intr_flag && status == 0 && thelock->locked) {
/* We were woken up, but didn't get the lock. We probably received
* a signal. Return PY_LOCK_INTR to allow the caller to handle
* it and retry. */
success = PY_LOCK_INTR;
break;
}
else if (status == 0 && !thelock->locked) {
success = PY_LOCK_ACQUIRED;
}
}
}
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]");
}
if (error) success = PY_LOCK_FAILURE;
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",
lock, microseconds, intr_flag, success));
return success;
}
void
PyThread_release_lock(PyThread_type_lock lock)
{
pthread_lock *thelock = (pthread_lock *)lock;
int status, error = 0;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_release_lock(%p) called\n", lock));
status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_lock[3]");
thelock->locked = 0;
/* wake up someone (anyone, if any) waiting on the lock */
status = pthread_cond_signal( &thelock->lock_released );
CHECK_STATUS_PTHREAD("pthread_cond_signal");
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_unlock[3]");
}
#endif /* USE_SEMAPHORES */
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
{
return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0, /*intr_flag=*/0);
}
/* set the thread stack size.
* Return 0 if size is valid, -1 if size is invalid,
* -2 if setting stack size is not supported.
*/
static int
_pythread_pthread_set_stacksize(size_t size)
{
#if defined(THREAD_STACK_SIZE)
pthread_attr_t attrs;
size_t tss_min;
int rc = 0;
#endif
/* set to default */
if (size == 0) {
_pythread_stacksize = 0;
return 0;
}
#if defined(THREAD_STACK_SIZE)
#if defined(PTHREAD_STACK_MIN)
tss_min = PTHREAD_STACK_MIN > THREAD_STACK_MIN ? PTHREAD_STACK_MIN
: THREAD_STACK_MIN;
#else
tss_min = THREAD_STACK_MIN;
#endif
if (size >= tss_min) {
/* validate stack size by setting thread attribute */
if (pthread_attr_init(&attrs) == 0) {
rc = pthread_attr_setstacksize(&attrs, size);
pthread_attr_destroy(&attrs);
if (rc == 0) {
_pythread_stacksize = size;
return 0;
}
}
}
return -1;
#else
return -2;
#endif
}
#define THREAD_SET_STACKSIZE(x) _pythread_pthread_set_stacksize(x)
#define Py_HAVE_NATIVE_TLS
int
PyThread_create_key(void)
{
pthread_key_t key;
int fail = pthread_key_create(&key, NULL);
if (fail)
return -1;
if (key > INT_MAX) {
/* Issue #22206: handle integer overflow */
pthread_key_delete(key);
errno = ENOMEM;
return -1;
}
return (int)key;
}
void
PyThread_delete_key(int key)
{
pthread_key_delete(key);
}
void
PyThread_delete_key_value(int key)
{
pthread_setspecific(key, NULL);
}
int
PyThread_set_key_value(int key, void *value)
{
int fail;
fail = pthread_setspecific(key, value);
return fail ? -1 : 0;
}
void *
PyThread_get_key_value(int key)
{
return pthread_getspecific(key);
}
void
PyThread_ReInitTLS(void)
{}