mirror of
https://github.com/janet-lang/janet
synced 2025-07-07 20:42:54 +00:00
Port threads code to Windows API
Can run demo in examples/threads.janet
This commit is contained in:
parent
4187c972a3
commit
38f7e256d0
@ -433,9 +433,10 @@ static Janet os_time(int32_t argc, Janet *argv) {
|
|||||||
/* Clock shims */
|
/* Clock shims */
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
static int gettime(struct timespec *spec) {
|
static int gettime(struct timespec *spec) {
|
||||||
int64_t wintime = 0LL;
|
FILETIME ftime;
|
||||||
GetSystemTimeAsFileTime((FILETIME *)&wintime);
|
GetSystemTimeAsFileTime(&ftime);
|
||||||
/* Windows epoch is January 1, 1601 apparently*/
|
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
|
||||||
|
/* Windows epoch is January 1, 1601 apparently */
|
||||||
wintime -= 116444736000000000LL;
|
wintime -= 116444736000000000LL;
|
||||||
spec->tv_sec = wintime / 10000000LL;
|
spec->tv_sec = wintime / 10000000LL;
|
||||||
/* Resolution is 100 nanoseconds. */
|
/* Resolution is 100 nanoseconds. */
|
||||||
|
@ -29,16 +29,25 @@
|
|||||||
|
|
||||||
#ifdef JANET_THREADS
|
#ifdef JANET_THREADS
|
||||||
|
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* typedefed in janet.h */
|
/* typedefed in janet.h */
|
||||||
struct JanetMailbox {
|
struct JanetMailbox {
|
||||||
|
|
||||||
/* Synchronization */
|
/* Synchronization */
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
CRITICAL_SECTION lock;
|
||||||
|
CONDITION_VARIABLE cond;
|
||||||
|
#else
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Receiving messages - (only by owner thread) */
|
/* Receiving messages - (only by owner thread) */
|
||||||
JanetTable *decode;
|
JanetTable *decode;
|
||||||
@ -69,8 +78,13 @@ static JanetMailbox *janet_mailbox_create(JanetMailbox *parent, int refCount, ui
|
|||||||
if (NULL == mailbox) {
|
if (NULL == mailbox) {
|
||||||
JANET_OUT_OF_MEMORY;
|
JANET_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
InitializeCriticalSection(&mailbox->lock);
|
||||||
|
InitializeConditionVariable(&mailbox->cond);
|
||||||
|
#else
|
||||||
pthread_mutex_init(&mailbox->lock, NULL);
|
pthread_mutex_init(&mailbox->lock, NULL);
|
||||||
pthread_cond_init(&mailbox->cond, NULL);
|
pthread_cond_init(&mailbox->cond, NULL);
|
||||||
|
#endif
|
||||||
mailbox->refCount = refCount;
|
mailbox->refCount = refCount;
|
||||||
mailbox->closed = 0;
|
mailbox->closed = 0;
|
||||||
mailbox->parent = parent;
|
mailbox->parent = parent;
|
||||||
@ -85,26 +99,47 @@ static JanetMailbox *janet_mailbox_create(JanetMailbox *parent, int refCount, ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void janet_mailbox_destroy(JanetMailbox *mailbox) {
|
static void janet_mailbox_destroy(JanetMailbox *mailbox) {
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
DeleteCriticalSection(&mailbox->lock);
|
||||||
|
#else
|
||||||
pthread_mutex_destroy(&mailbox->lock);
|
pthread_mutex_destroy(&mailbox->lock);
|
||||||
pthread_cond_destroy(&mailbox->cond);
|
pthread_cond_destroy(&mailbox->cond);
|
||||||
|
#endif
|
||||||
for (uint16_t i = 0; i < mailbox->messageCapacity; i++) {
|
for (uint16_t i = 0; i < mailbox->messageCapacity; i++) {
|
||||||
janet_buffer_deinit(mailbox->messages + i);
|
janet_buffer_deinit(mailbox->messages + i);
|
||||||
}
|
}
|
||||||
free(mailbox);
|
free(mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void janet_mailbox_lock(JanetMailbox *mailbox) {
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
EnterCriticalSection(&mailbox->lock);
|
||||||
|
#else
|
||||||
|
pthread_mutex_lock(&mailbox->lock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void janet_mailbox_unlock(JanetMailbox *mailbox) {
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
LeaveCriticalSection(&mailbox->lock);
|
||||||
|
#else
|
||||||
|
pthread_mutex_unlock(&mailbox->lock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Assumes you have the mailbox lock already */
|
/* Assumes you have the mailbox lock already */
|
||||||
static void janet_mailbox_ref_with_lock(JanetMailbox *mailbox, int delta) {
|
static void janet_mailbox_ref_with_lock(JanetMailbox *mailbox, int delta) {
|
||||||
mailbox->refCount += delta;
|
mailbox->refCount += delta;
|
||||||
if (mailbox->refCount <= 0) {
|
if (mailbox->refCount <= 0) {
|
||||||
|
janet_mailbox_unlock(mailbox);
|
||||||
janet_mailbox_destroy(mailbox);
|
janet_mailbox_destroy(mailbox);
|
||||||
} else {
|
} else {
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
janet_mailbox_unlock(mailbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void janet_mailbox_ref(JanetMailbox *mailbox, int delta) {
|
static void janet_mailbox_ref(JanetMailbox *mailbox, int delta) {
|
||||||
pthread_mutex_lock(&mailbox->lock);
|
janet_mailbox_lock(mailbox);
|
||||||
janet_mailbox_ref_with_lock(mailbox, delta);
|
janet_mailbox_ref_with_lock(mailbox, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,25 +166,91 @@ static int thread_mark(void *p, size_t size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert an interval from now in an absolute timespec */
|
/* Abstract waiting for timeout across windows/posix */
|
||||||
static void janet_sec2ts(double sec, struct timespec *ts) {
|
typedef struct {
|
||||||
struct timespec now;
|
int timedwait;
|
||||||
clock_gettime(CLOCK_REALTIME, &now);
|
int nowait;
|
||||||
time_t tvsec = (time_t) floor(sec);
|
#ifdef JANET_WINDOWS
|
||||||
long tvnsec = (long) floor(1000000000.0 * (sec - ((double) tvsec)));
|
DWORD interval;
|
||||||
tvsec += now.tv_sec;
|
DWORD ticksLeft;
|
||||||
tvnsec += now.tv_nsec;
|
#else
|
||||||
if (tvnsec >= 1000000000L) {
|
struct timespec ts;
|
||||||
tvnsec -= 1000000000L;
|
#endif
|
||||||
tvsec += 1;
|
} JanetWaiter;
|
||||||
|
|
||||||
|
static void janet_waiter_init(JanetWaiter *waiter, double sec) {
|
||||||
|
waiter->timedwait = 0;
|
||||||
|
waiter->nowait = 0;
|
||||||
|
|
||||||
|
if (sec == 0.0 || isnan(sec)) {
|
||||||
|
waiter->nowait = 1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
ts->tv_sec = tvsec;
|
waiter->timedwait = sec > 0.0;
|
||||||
ts->tv_nsec = tvnsec;
|
|
||||||
|
/* Set maximum wait time to 30 days */
|
||||||
|
if (sec > (60.0 * 60.0 * 24.0 * 30.0)) {
|
||||||
|
sec = 60.0 * 60.0 * 24.0 * 30.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
if (waiter->timedwait) {
|
||||||
|
waiter->ticksLeft = waiter->interval = (DWORD) floor(1000.0 * sec);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (waiter->timedwait) {
|
||||||
|
/* N seconds -> timespec of (now + sec) */
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
time_t tvsec = (time_t) floor(sec);
|
||||||
|
long tvnsec = (long) floor(1000000000.0 * (sec - ((double) tvsec)));
|
||||||
|
tvsec += now.tv_sec;
|
||||||
|
tvnsec += now.tv_nsec;
|
||||||
|
if (tvnsec >= 1000000000L) {
|
||||||
|
tvnsec -= 1000000000L;
|
||||||
|
tvsec += 1;
|
||||||
|
}
|
||||||
|
waiter->ts.tv_sec = tvsec;
|
||||||
|
waiter->ts.tv_nsec = tvnsec;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int janet_waiter_wait(JanetWaiter *wait, JanetMailbox *mailbox) {
|
||||||
|
if (wait->nowait) return 1;
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
if (wait->timedwait) {
|
||||||
|
if (wait->ticksLeft == 0) return 1;
|
||||||
|
DWORD startTime = GetTickCount();
|
||||||
|
int status = !SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, wait->ticksLeft);
|
||||||
|
DWORD dTick = GetTickCount() - startTime;
|
||||||
|
/* Be careful about underflow */
|
||||||
|
wait->ticksLeft = dTick > wait->ticksLeft ? 0 : dTick;
|
||||||
|
return status;
|
||||||
|
} else {
|
||||||
|
SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, INFINITE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (wait->timedwait) {
|
||||||
|
return pthread_cond_timedwait(&mailbox->cond, &mailbox->lock, &wait->ts);
|
||||||
|
} else {
|
||||||
|
pthread_cond_wait(&mailbox->cond, &mailbox->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void janet_mailbox_wakeup(JanetMailbox *mailbox) {
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
WakeConditionVariable(&mailbox->cond);
|
||||||
|
#else
|
||||||
|
pthread_cond_signal(&mailbox->cond);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mailbox_at_capacity(JanetMailbox *mailbox) {
|
static int mailbox_at_capacity(JanetMailbox *mailbox) {
|
||||||
return mailbox->messageCapacity > 0
|
return mailbox->messageCount >= mailbox->messageCapacity;
|
||||||
&& mailbox->messageCount == mailbox->messageCapacity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if could not send (encode error or timeout), 2 for mailbox closed, and
|
/* Returns 1 if could not send (encode error or timeout), 2 for mailbox closed, and
|
||||||
@ -159,35 +260,32 @@ int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
|
|||||||
/* Ensure mailbox is not closed. */
|
/* Ensure mailbox is not closed. */
|
||||||
JanetMailbox *mailbox = thread->mailbox;
|
JanetMailbox *mailbox = thread->mailbox;
|
||||||
if (NULL == mailbox) return 2;
|
if (NULL == mailbox) return 2;
|
||||||
pthread_mutex_lock(&mailbox->lock);
|
janet_mailbox_lock(mailbox);
|
||||||
if (mailbox->closed) {
|
if (mailbox->closed) {
|
||||||
janet_mailbox_ref_with_lock(mailbox, -1);
|
janet_mailbox_ref_with_lock(mailbox, -1);
|
||||||
thread->mailbox = NULL;
|
thread->mailbox = NULL;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int didWait = 0;
|
||||||
|
|
||||||
/* Back pressure */
|
/* Back pressure */
|
||||||
if (mailbox_at_capacity(mailbox)) {
|
if (mailbox_at_capacity(mailbox)) {
|
||||||
struct timespec timeout_ts;
|
JanetWaiter wait;
|
||||||
int timedwait = timeout > 0.0;
|
janet_waiter_init(&wait, timeout);
|
||||||
int nowait = timeout == 0.0;
|
|
||||||
if (timedwait) janet_sec2ts(timeout, &timeout_ts);
|
if (wait.nowait) {
|
||||||
if (nowait) {
|
janet_mailbox_unlock(mailbox);
|
||||||
janet_mailbox_ref_with_lock(mailbox, -1);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retry loop, as there can be multiple writers */
|
/* Retry loop, as there can be multiple writers */
|
||||||
while (mailbox->messageCount > mailbox->messageCapacity) {
|
while (mailbox_at_capacity(mailbox)) {
|
||||||
if (timedwait) {
|
didWait = 1;
|
||||||
int status = pthread_cond_timedwait(&mailbox->cond, &mailbox->lock, &timeout_ts);
|
if (janet_waiter_wait(&wait, mailbox)) {
|
||||||
if (status) {
|
janet_mailbox_unlock(mailbox);
|
||||||
/* Timedout */
|
janet_mailbox_wakeup(mailbox);
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pthread_cond_wait(&mailbox->cond, &mailbox->lock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,12 +315,10 @@ int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
|
|||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
janet_vm_jmp_buf = old_buf;
|
janet_vm_jmp_buf = old_buf;
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
janet_mailbox_unlock(mailbox);
|
||||||
|
|
||||||
/* Potentially wake up a blocked thread */
|
/* Potentially wake up a blocked thread */
|
||||||
if (oldmcount == 0 && ret == 0) {
|
if (didWait || (oldmcount == 0 && ret == 0)) janet_mailbox_wakeup(mailbox);
|
||||||
pthread_cond_signal(&mailbox->cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -230,13 +326,11 @@ int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
|
|||||||
/* Returns 0 on successful message. Returns 1 if timedout */
|
/* Returns 0 on successful message. Returns 1 if timedout */
|
||||||
int janet_thread_receive(Janet *msg_out, double timeout) {
|
int janet_thread_receive(Janet *msg_out, double timeout) {
|
||||||
JanetMailbox *mailbox = janet_vm_mailbox;
|
JanetMailbox *mailbox = janet_vm_mailbox;
|
||||||
pthread_mutex_lock(&mailbox->lock);
|
janet_mailbox_lock(mailbox);
|
||||||
|
|
||||||
/* For timeouts */
|
/* For timeouts */
|
||||||
struct timespec timeout_ts;
|
JanetWaiter wait;
|
||||||
int timedwait = timeout > 0.0;
|
janet_waiter_init(&wait, timeout);
|
||||||
int nowait = timeout == 0.0;
|
|
||||||
if (timedwait) janet_sec2ts(timeout, &timeout_ts);
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
@ -271,38 +365,24 @@ int janet_thread_receive(Janet *msg_out, double timeout) {
|
|||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
janet_vm_jmp_buf = old_buf;
|
janet_vm_jmp_buf = old_buf;
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
janet_mailbox_unlock(mailbox);
|
||||||
|
|
||||||
/* Wake up pending writers */
|
/* Wake up pending writers */
|
||||||
if (wasAtCapacity) {
|
if (wasAtCapacity) janet_mailbox_wakeup(mailbox);
|
||||||
pthread_cond_signal(&mailbox->cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nowait || mailbox->refCount <= 1) {
|
if (wait.nowait || mailbox->refCount <= 1) {
|
||||||
/* If there is only one ref, it is us. This means that if we
|
janet_mailbox_unlock(mailbox);
|
||||||
* start waiting now, we can never possibly get a message, as
|
|
||||||
* our reference will not propogate to other threads while we are blocked. */
|
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for next message */
|
/* Wait for next message */
|
||||||
if (timedwait) {
|
if (janet_waiter_wait(&wait, mailbox)) {
|
||||||
if (pthread_cond_timedwait(
|
janet_mailbox_unlock(mailbox);
|
||||||
&mailbox->cond,
|
return 1;
|
||||||
&mailbox->lock,
|
|
||||||
&timeout_ts)) {
|
|
||||||
pthread_mutex_unlock(&mailbox->lock);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pthread_cond_wait(
|
|
||||||
&mailbox->cond,
|
|
||||||
&mailbox->lock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,11 +470,28 @@ static int thread_worker(JanetMailbox *mailbox) {
|
|||||||
|
|
||||||
/* Fail to set something up */
|
/* Fail to set something up */
|
||||||
error:
|
error:
|
||||||
janet_eprintf("thread failed to start\n");
|
janet_eprintf("\nthread failed to start\n");
|
||||||
janet_deinit();
|
janet_deinit();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
|
||||||
|
static DWORD janet_create_thread_wrapper(void *param) {
|
||||||
|
thread_worker((JanetMailbox *)param);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int janet_thread_start_child(JanetThread *thread) {
|
||||||
|
HANDLE handle = CreateThread(NULL, 0, janet_create_thread_wrapper, thread->mailbox, 0, NULL);
|
||||||
|
int ret = NULL == handle;
|
||||||
|
/* Does not kill thread, simply detatches */
|
||||||
|
if (!ret) CloseHandle(handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
static void *janet_pthread_wrapper(void *param) {
|
static void *janet_pthread_wrapper(void *param) {
|
||||||
thread_worker((JanetMailbox *)param);
|
thread_worker((JanetMailbox *)param);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -411,6 +508,8 @@ static int janet_thread_start_child(JanetThread *thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup/Teardown
|
* Setup/Teardown
|
||||||
*/
|
*/
|
||||||
@ -422,7 +521,7 @@ void janet_threads_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void janet_threads_deinit(void) {
|
void janet_threads_deinit(void) {
|
||||||
pthread_mutex_lock(&janet_vm_mailbox->lock);
|
janet_mailbox_lock(janet_vm_mailbox);
|
||||||
janet_vm_mailbox->closed = 1;
|
janet_vm_mailbox->closed = 1;
|
||||||
janet_mailbox_ref_with_lock(janet_vm_mailbox, -1);
|
janet_mailbox_ref_with_lock(janet_vm_mailbox, -1);
|
||||||
janet_vm_mailbox = NULL;
|
janet_vm_mailbox = NULL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user