blob: 0112efa1696285785710cfbb49e785c49c21cafd [file] [log] [blame]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
/* This is a minimal pthread implementation based on windows functions.
* It is *not* intended to be complete - just complete enough to get
* BRLTTY running.
*/
#ifndef BRLTTY_INCLUDED_WIN_PTHREAD
#define BRLTTY_INCLUDED_WIN_PTHREAD
#include "prologue.h"
#include <windows.h>
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include "gettime.h"
#ifndef ETIMEDOUT
#define ETIMEDOUT EAGAIN
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define winPthreadAssertWindows(expr) do { if (!(expr)) { setSystemErrno(); return errno; } } while (0)
#define winPthreadAssertPthread(expr) do { int ret = (expr); if (ret) return ret; } while (0)
#define winPthreadAssert(expr) do { if (!(expr)) return EIO; } while (0)
#if !defined(__struct_timespec_defined) && !defined(__MINGW64_VERSION_MAJOR)
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
#endif /* struct timespec */
static inline DWORD pthread_gettimeout_np(const struct timespec *abs_timeout) {
struct timeval now;
LONG timeout;
getRealTime(&now);
timeout = (abs_timeout->tv_sec - now.tv_sec) * 1000 +
(abs_timeout->tv_nsec - now.tv_usec * 1000) / 1000000;
if (timeout < 0)
timeout = 0;
return timeout;
}
/***********
* threads *
***********/
typedef DWORD pthread_attr_t;
typedef HANDLE pthread_t;
static inline pthread_t pthread_self(void) {
return GetCurrentThread();
}
static inline int pthread_equal(pthread_t t1, pthread_t t2) {
return t1 == t2;
}
static inline int pthread_attr_init (pthread_attr_t *attr) {
*attr = 0;
return 0;
}
#define PTHREAD_CREATE_DETACHED 1
static inline int pthread_attr_setdetachstate (pthread_attr_t *attr, int yes) {
/* not supported, ignore */
return 0;
}
static inline int pthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize) {
/* not supported, ignore */
return 0;
}
static inline int pthread_attr_destroy (pthread_attr_t *attr) {
return 0;
}
/* "real" cleanup handling not yet implemented */
typedef struct {
void (*routine) (void *);
void *arg;
} __pthread_cleanup_handler;
void pthread_cleanup_push (void (*routine) (void *), void *arg);
#define pthread_cleanup_push(routine, arg) do { \
__pthread_cleanup_handler __cleanup_handler = {routine, arg};
void pthread_cleanup_pop (int execute);
#define pthread_cleanup_pop(execute) \
if (execute) __cleanup_handler.routine(__cleanup_handler.arg); \
} while (0);
static inline int pthread_create (
pthread_t *thread, const pthread_attr_t *attr,
void * (*fun) (void *), void *arg
) {
if (attr && *attr)
return EINVAL;
winPthreadAssertWindows(*thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) fun, arg, 0, NULL));
return 0;
}
static inline int pthread_setcancelstate (int state, int *oldstate) {
/* not yet implemented :( */
return 0;
}
static inline int pthread_cancel (pthread_t thread) {
/* This is quite harsh :( */
winPthreadAssertWindows(TerminateThread(thread, 0));
return 0;
}
static inline void pthread_exit (void *res) {
ExitThread((DWORD) (DWORD_PTR) res);
}
static inline int pthread_join (pthread_t thread, void **res) {
again:
switch (WaitForSingleObject(thread, INFINITE)) {
default:
case WAIT_FAILED:
setSystemErrno();
return errno;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
goto again;
}
if (res) {
DWORD _res;
if (GetExitCodeThread(thread, &_res))
*res = (void *)(DWORD_PTR)_res;
}
return 0;
}
/***********
* mutexes *
***********/
#define PTHREAD_MUTEX_INITIALIZER NULL
typedef HANDLE pthread_mutex_t;
#define PTHREAD_MUTEX_RECURSIVE 1
typedef int pthread_mutexattr_t;
static inline int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
*attr = PTHREAD_MUTEX_RECURSIVE;
return 0;
}
static inline int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
if (type != PTHREAD_MUTEX_RECURSIVE)
return EINVAL;
*attr = type;
return 0;
}
static inline int pthread_mutex_init (pthread_mutex_t *mutex, pthread_mutexattr_t *attr) {
if (attr && *attr!=PTHREAD_MUTEX_RECURSIVE)
return EINVAL;
winPthreadAssertWindows(*mutex = CreateMutex(NULL, FALSE, NULL));
return 0;
}
static inline int pthread_mutex_unlock (pthread_mutex_t *mutex) {
winPthreadAssertWindows(ReleaseMutex(*mutex));
return 0;
}
static inline int pthread_mutex_lock (pthread_mutex_t *mutex);
static inline int __pthread_mutex_alloc_concurrently (pthread_mutex_t *mutex) {
HANDLE mutex_init_mutex;
/* Get access to one global named mutex to serialize mutex initialization */
winPthreadAssertWindows((mutex_init_mutex = CreateMutex(NULL, FALSE, "StarPU mutex init")));
winPthreadAssertPthread(pthread_mutex_lock(&mutex_init_mutex));
/* Now we are the one that can initialize it */
if (!*mutex)
winPthreadAssertPthread(pthread_mutex_init(mutex,NULL));
winPthreadAssertPthread(pthread_mutex_unlock(&mutex_init_mutex));
winPthreadAssertWindows(CloseHandle(mutex_init_mutex));
return 0;
}
static inline int pthread_mutex_lock (pthread_mutex_t *mutex) {
if (!*mutex)
__pthread_mutex_alloc_concurrently (mutex);
again:
switch (WaitForSingleObject(*mutex, INFINITE)) {
default:
case WAIT_FAILED:
setSystemErrno();
return errno;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return 0;
case WAIT_TIMEOUT:
goto again;
}
}
static inline int pthread_mutex_trylock (pthread_mutex_t *mutex) {
if (!*mutex)
__pthread_mutex_alloc_concurrently (mutex);
switch (WaitForSingleObject(*mutex, 0)) {
default:
case WAIT_FAILED:
setSystemErrno();
return errno;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return 0;
case WAIT_TIMEOUT:
return EBUSY;
}
}
static inline int pthread_mutex_destroy (pthread_mutex_t *mutex) {
winPthreadAssertWindows(CloseHandle(*mutex));
*mutex = INVALID_HANDLE_VALUE;
return 0;
}
/**************
* semaphores *
**************/
typedef HANDLE sem_t;
static inline int sem_init(sem_t *sem, int pshared, unsigned int value) {
winPthreadAssertWindows(*sem = CreateSemaphore(NULL, value, MAXLONG, NULL));
return 0;
}
static inline int do_sem_wait(sem_t *sem, DWORD timeout) {
switch (WaitForSingleObject(*sem, timeout)) {
default:
case WAIT_FAILED:
setSystemErrno();
return -1;
case WAIT_TIMEOUT:
errno = EAGAIN;
return -1;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return 0;
}
}
#define sem_wait(sem) do_sem_wait(sem, INFINITE)
#define sem_trywait(sem) do_sem_wait(sem, 0)
static inline int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) {
return do_sem_wait(sem, pthread_gettimeout_np(abs_timeout));
}
static inline int sem_post(sem_t *sem) {
winPthreadAssertWindows(ReleaseSemaphore(*sem, 1, NULL));
return 0;
}
static inline int sem_destroy(sem_t *sem) {
winPthreadAssertWindows(CloseHandle(*sem));
return 0;
}
/**************
* conditions *
**************/
typedef struct {
HANDLE sem;
volatile unsigned nbwait;
} pthread_cond_t;
#define PTHREAD_COND_INITIALIZER { NULL, 0}
typedef unsigned pthread_condattr_t;
static inline int pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr) {
if (attr)
return EINVAL;
winPthreadAssertWindows(cond->sem = CreateSemaphore(NULL, 0, MAXLONG, NULL));
cond->nbwait = 0;
return 0;
}
static inline int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *time) {
if (!cond->sem)
winPthreadAssertPthread(pthread_cond_init(cond,NULL));
cond->nbwait++;
winPthreadAssertPthread(pthread_mutex_unlock(mutex));
switch (WaitForSingleObject(cond->sem, pthread_gettimeout_np(time))) {
default:
case WAIT_FAILED:
setSystemErrno();
winPthreadAssertPthread(pthread_mutex_lock(mutex));
return errno;
case WAIT_TIMEOUT:
winPthreadAssertPthread(pthread_mutex_lock(mutex));
return ETIMEDOUT;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
break;
}
winPthreadAssertPthread(pthread_mutex_lock(mutex));
cond->nbwait--;
return 0;
}
static inline int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) {
if (!cond->sem)
winPthreadAssertPthread(pthread_cond_init(cond,NULL));
cond->nbwait++;
winPthreadAssertPthread(pthread_mutex_unlock(mutex));
again:
switch (WaitForSingleObject(cond->sem, INFINITE)) {
case WAIT_FAILED:
setSystemErrno();
winPthreadAssert(!pthread_mutex_lock(mutex));
return errno;
case WAIT_TIMEOUT:
goto again;
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
break;
}
winPthreadAssertPthread(pthread_mutex_lock(mutex));
cond->nbwait--;
return 0;
}
static inline int pthread_cond_signal (pthread_cond_t *cond) {
if (!cond->sem)
winPthreadAssertPthread(pthread_cond_init(cond,NULL));
if (cond->nbwait)
ReleaseSemaphore(cond->sem, 1, NULL);
return 0;
}
static inline int pthread_cond_broadcast (pthread_cond_t *cond) {
if (!cond->sem)
winPthreadAssertPthread(pthread_cond_init(cond,NULL));
ReleaseSemaphore(cond->sem, cond->nbwait, NULL);
return 0;
}
static inline int pthread_cond_destroy (pthread_cond_t *cond) {
if (cond->sem) {
winPthreadAssertWindows(CloseHandle(cond->sem));
cond->sem = NULL;
}
return 0;
}
/*******
* TLS *
*******/
typedef DWORD pthread_key_t;
#define PTHREAD_ONCE_INIT {PTHREAD_MUTEX_INITIALIZER, 0}
typedef struct {
pthread_mutex_t mutex;
unsigned done;
} pthread_once_t;
static inline int pthread_once (pthread_once_t *once, void (*oncefun)(void)) {
winPthreadAssertPthread(pthread_mutex_lock(&once->mutex));
if (!once->done) {
oncefun();
once->done = 1;
}
winPthreadAssertPthread(pthread_mutex_unlock(&once->mutex));
return 0;
}
static inline int pthread_key_create (pthread_key_t *key, void (*freefun)(void *)) {
DWORD res;
winPthreadAssertWindows((res = TlsAlloc()) != 0xFFFFFFFF);
*key = res;
return 0;
}
static inline int pthread_key_delete (pthread_key_t key) {
winPthreadAssertWindows(TlsFree(key));
return 0;
}
static inline void *pthread_getspecific (pthread_key_t key) {
return TlsGetValue(key);
}
static inline int pthread_setspecific (pthread_key_t key, const void *data) {
winPthreadAssertWindows(TlsSetValue(key, (LPVOID) data));
return 0;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BRLTTY_INCLUDED_WIN_PTHREAD */