2019-11-27 05:13:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Calvin Rose
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef JANET_AMALG
|
|
|
|
#include <janet.h>
|
|
|
|
#include "gc.h"
|
|
|
|
#include "util.h"
|
2019-12-02 02:28:12 +00:00
|
|
|
#include "state.h"
|
2019-11-27 05:13:53 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef JANET_THREADS
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
2019-12-02 02:28:12 +00:00
|
|
|
#include <setjmp.h>
|
2019-12-06 15:21:36 +00:00
|
|
|
#include <time.h>
|
2019-12-07 18:14:16 +00:00
|
|
|
#include <pthread.h>
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
2019-12-07 18:14:16 +00:00
|
|
|
|
|
|
|
/* typedefed in janet.h */
|
|
|
|
struct JanetMailbox {
|
2019-12-08 18:30:30 +00:00
|
|
|
|
|
|
|
/* Synchronization */
|
2019-12-11 01:32:41 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
CRITICAL_SECTION lock;
|
|
|
|
CONDITION_VARIABLE cond;
|
|
|
|
#else
|
2019-12-07 18:14:16 +00:00
|
|
|
pthread_mutex_t lock;
|
|
|
|
pthread_cond_t cond;
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
2019-12-08 18:30:30 +00:00
|
|
|
|
|
|
|
/* Receiving messages - (only by owner thread) */
|
|
|
|
JanetTable *decode;
|
|
|
|
|
|
|
|
/* Setup procedure - requires a parent mailbox
|
|
|
|
* to receive thunk from */
|
|
|
|
JanetMailbox *parent;
|
|
|
|
|
|
|
|
/* Memory management - reference counting */
|
2019-12-07 18:14:16 +00:00
|
|
|
int refCount;
|
|
|
|
int closed;
|
2019-12-10 19:26:00 +00:00
|
|
|
|
|
|
|
/* Store messages */
|
|
|
|
uint16_t messageCapacity;
|
|
|
|
uint16_t messageCount;
|
|
|
|
uint16_t messageFirst;
|
|
|
|
uint16_t messageNext;
|
|
|
|
|
|
|
|
/* Buffers to store messages */
|
|
|
|
JanetBuffer messages[];
|
2019-12-07 18:14:16 +00:00
|
|
|
};
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
static JANET_THREAD_LOCAL JanetMailbox *janet_vm_mailbox = NULL;
|
2019-12-08 18:30:30 +00:00
|
|
|
static JANET_THREAD_LOCAL JanetThread *janet_vm_thread_current = NULL;
|
2019-12-05 03:44:53 +00:00
|
|
|
|
2019-12-10 19:26:00 +00:00
|
|
|
static JanetMailbox *janet_mailbox_create(JanetMailbox *parent, int refCount, uint16_t capacity) {
|
|
|
|
JanetMailbox *mailbox = malloc(sizeof(JanetMailbox) + sizeof(JanetBuffer) * capacity);
|
2019-12-07 22:51:00 +00:00
|
|
|
if (NULL == mailbox) {
|
2019-12-07 18:14:16 +00:00
|
|
|
JANET_OUT_OF_MEMORY;
|
|
|
|
}
|
2019-12-11 01:32:41 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
InitializeCriticalSection(&mailbox->lock);
|
|
|
|
InitializeConditionVariable(&mailbox->cond);
|
|
|
|
#else
|
2019-12-07 22:51:00 +00:00
|
|
|
pthread_mutex_init(&mailbox->lock, NULL);
|
|
|
|
pthread_cond_init(&mailbox->cond, NULL);
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
2019-12-07 22:51:00 +00:00
|
|
|
mailbox->refCount = refCount;
|
|
|
|
mailbox->closed = 0;
|
|
|
|
mailbox->parent = parent;
|
2019-12-08 18:30:30 +00:00
|
|
|
mailbox->messageCount = 0;
|
2019-12-10 19:26:00 +00:00
|
|
|
mailbox->messageCapacity = capacity;
|
|
|
|
mailbox->messageFirst = 0;
|
|
|
|
mailbox->messageNext = 0;
|
|
|
|
for (uint16_t i = 0; i < capacity; i++) {
|
|
|
|
janet_buffer_init(mailbox->messages + i, 0);
|
|
|
|
}
|
2019-12-07 22:51:00 +00:00
|
|
|
return mailbox;
|
2019-12-07 18:14:16 +00:00
|
|
|
}
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
static void janet_mailbox_destroy(JanetMailbox *mailbox) {
|
2019-12-11 01:32:41 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
DeleteCriticalSection(&mailbox->lock);
|
|
|
|
#else
|
2019-12-07 22:51:00 +00:00
|
|
|
pthread_mutex_destroy(&mailbox->lock);
|
|
|
|
pthread_cond_destroy(&mailbox->cond);
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
2019-12-10 19:26:00 +00:00
|
|
|
for (uint16_t i = 0; i < mailbox->messageCapacity; i++) {
|
|
|
|
janet_buffer_deinit(mailbox->messages + i);
|
|
|
|
}
|
2019-12-07 22:51:00 +00:00
|
|
|
free(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-12-07 18:14:16 +00:00
|
|
|
/* Assumes you have the mailbox lock already */
|
2019-12-07 22:51:00 +00:00
|
|
|
static void janet_mailbox_ref_with_lock(JanetMailbox *mailbox, int delta) {
|
|
|
|
mailbox->refCount += delta;
|
|
|
|
if (mailbox->refCount <= 0) {
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_mailbox_destroy(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
} else {
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
static void janet_mailbox_ref(JanetMailbox *mailbox, int delta) {
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_lock(mailbox);
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_mailbox_ref_with_lock(mailbox, delta);
|
2019-12-05 03:44:53 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
static void janet_close_thread(JanetThread *thread) {
|
2019-12-07 18:14:16 +00:00
|
|
|
if (thread->mailbox) {
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_mailbox_ref(thread->mailbox, -1);
|
2019-12-07 18:14:16 +00:00
|
|
|
thread->mailbox = NULL;
|
2019-12-06 07:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-02 02:28:12 +00:00
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
static int thread_gc(void *p, size_t size) {
|
|
|
|
(void) size;
|
|
|
|
JanetThread *thread = (JanetThread *)p;
|
|
|
|
janet_close_thread(thread);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int thread_mark(void *p, size_t size) {
|
2019-12-07 22:51:00 +00:00
|
|
|
(void) size;
|
2019-12-06 07:46:23 +00:00
|
|
|
JanetThread *thread = (JanetThread *)p;
|
2019-12-07 18:14:16 +00:00
|
|
|
if (thread->encode) {
|
|
|
|
janet_mark(janet_wrap_table(thread->encode));
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
2019-12-06 07:46:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
/* Abstract waiting for timeout across windows/posix */
|
|
|
|
typedef struct {
|
|
|
|
int timedwait;
|
|
|
|
int nowait;
|
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
DWORD interval;
|
|
|
|
DWORD ticksLeft;
|
|
|
|
#else
|
|
|
|
struct timespec ts;
|
|
|
|
#endif
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
waiter->timedwait = sec > 0.0;
|
|
|
|
|
|
|
|
/* 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;
|
2019-12-08 18:30:30 +00:00
|
|
|
}
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void janet_mailbox_wakeup(JanetMailbox *mailbox) {
|
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
WakeConditionVariable(&mailbox->cond);
|
|
|
|
#else
|
|
|
|
pthread_cond_signal(&mailbox->cond);
|
|
|
|
#endif
|
2019-12-08 18:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mailbox_at_capacity(JanetMailbox *mailbox) {
|
2019-12-11 01:32:41 +00:00
|
|
|
return mailbox->messageCount >= mailbox->messageCapacity;
|
2019-12-08 18:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if could not send (encode error or timeout), 2 for mailbox closed, and
|
2019-12-07 18:14:16 +00:00
|
|
|
* 0 otherwise. Will not panic. */
|
2019-12-08 18:30:30 +00:00
|
|
|
int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
|
2019-12-07 18:14:16 +00:00
|
|
|
|
|
|
|
/* Ensure mailbox is not closed. */
|
|
|
|
JanetMailbox *mailbox = thread->mailbox;
|
|
|
|
if (NULL == mailbox) return 2;
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_lock(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
if (mailbox->closed) {
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_mailbox_ref_with_lock(mailbox, -1);
|
|
|
|
thread->mailbox = NULL;
|
2019-12-07 18:14:16 +00:00
|
|
|
return 2;
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
int didWait = 0;
|
|
|
|
|
2019-12-08 18:30:30 +00:00
|
|
|
/* Back pressure */
|
|
|
|
if (mailbox_at_capacity(mailbox)) {
|
2019-12-11 01:32:41 +00:00
|
|
|
JanetWaiter wait;
|
|
|
|
janet_waiter_init(&wait, timeout);
|
|
|
|
|
|
|
|
if (wait.nowait) {
|
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-08 18:30:30 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retry loop, as there can be multiple writers */
|
2019-12-11 01:32:41 +00:00
|
|
|
while (mailbox_at_capacity(mailbox)) {
|
|
|
|
didWait = 1;
|
|
|
|
if (janet_waiter_wait(&wait, mailbox)) {
|
|
|
|
janet_mailbox_unlock(mailbox);
|
|
|
|
janet_mailbox_wakeup(mailbox);
|
|
|
|
return 1;
|
2019-12-08 18:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
/* Hack to capture all panics from marshalling. This works because
|
|
|
|
* we know janet_marshal won't mess with other essential global state. */
|
|
|
|
jmp_buf buf;
|
|
|
|
jmp_buf *old_buf = janet_vm_jmp_buf;
|
|
|
|
janet_vm_jmp_buf = &buf;
|
2019-12-08 18:30:30 +00:00
|
|
|
int32_t oldmcount = mailbox->messageCount;
|
2019-12-02 02:28:12 +00:00
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
if (setjmp(buf)) {
|
|
|
|
ret = 1;
|
2019-12-08 18:30:30 +00:00
|
|
|
mailbox->messageCount = oldmcount;
|
2019-12-02 02:28:12 +00:00
|
|
|
} else {
|
2019-12-10 19:26:00 +00:00
|
|
|
JanetBuffer *msgbuf = mailbox->messages + mailbox->messageNext;
|
|
|
|
msgbuf->count = 0;
|
|
|
|
|
|
|
|
/* Start panic zone */
|
|
|
|
janet_marshal(msgbuf, msg, thread->encode, 0);
|
|
|
|
/* End panic zone */
|
|
|
|
|
|
|
|
mailbox->messageNext = (mailbox->messageNext + 1) % mailbox->messageCapacity;
|
2019-12-08 18:30:30 +00:00
|
|
|
mailbox->messageCount++;
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
janet_vm_jmp_buf = old_buf;
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-07 22:51:00 +00:00
|
|
|
|
|
|
|
/* Potentially wake up a blocked thread */
|
2019-12-11 01:32:41 +00:00
|
|
|
if (didWait || (oldmcount == 0 && ret == 0)) janet_mailbox_wakeup(mailbox);
|
2019-12-02 02:28:12 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
/* Returns 0 on successful message. Returns 1 if timedout */
|
|
|
|
int janet_thread_receive(Janet *msg_out, double timeout) {
|
2019-12-08 18:30:30 +00:00
|
|
|
JanetMailbox *mailbox = janet_vm_mailbox;
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_lock(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
|
|
|
|
/* For timeouts */
|
2019-12-11 01:32:41 +00:00
|
|
|
JanetWaiter wait;
|
|
|
|
janet_waiter_init(&wait, timeout);
|
2019-12-07 18:14:16 +00:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
2019-12-08 18:30:30 +00:00
|
|
|
/* Check for messages waiting for us */
|
|
|
|
if (mailbox->messageCount > 0) {
|
|
|
|
|
|
|
|
/* If we were over capacity, receiving a message should
|
|
|
|
* bring us under capacity. We can therefor wake up pending writers. */
|
|
|
|
int wasAtCapacity = mailbox_at_capacity(mailbox);
|
|
|
|
|
2019-12-07 18:14:16 +00:00
|
|
|
/* Hack to capture all panics from marshalling. This works because
|
|
|
|
* we know janet_marshal won't mess with other essential global state. */
|
|
|
|
jmp_buf buf;
|
|
|
|
jmp_buf *old_buf = janet_vm_jmp_buf;
|
|
|
|
janet_vm_jmp_buf = &buf;
|
|
|
|
|
|
|
|
/* Handle errors */
|
|
|
|
if (setjmp(buf)) {
|
2019-12-08 18:30:30 +00:00
|
|
|
/* Cleanup jmp_buf, keep lock */
|
2019-12-07 18:14:16 +00:00
|
|
|
janet_vm_jmp_buf = old_buf;
|
|
|
|
} else {
|
2019-12-10 19:26:00 +00:00
|
|
|
JanetBuffer *msgbuf = mailbox->messages + mailbox->messageFirst;
|
|
|
|
mailbox->messageCount--;
|
|
|
|
mailbox->messageFirst = (mailbox->messageFirst + 1) % mailbox->messageCapacity;
|
|
|
|
|
2019-12-07 18:14:16 +00:00
|
|
|
/* Read from beginning of channel */
|
|
|
|
const uint8_t *nextItem = NULL;
|
|
|
|
Janet item = janet_unmarshal(
|
2019-12-10 19:26:00 +00:00
|
|
|
msgbuf->data, msgbuf->count,
|
2019-12-08 18:30:30 +00:00
|
|
|
0, mailbox->decode, &nextItem);
|
2019-12-07 18:14:16 +00:00
|
|
|
*msg_out = item;
|
2019-12-08 18:30:30 +00:00
|
|
|
|
|
|
|
/* Cleanup */
|
2019-12-07 18:14:16 +00:00
|
|
|
janet_vm_jmp_buf = old_buf;
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-08 18:30:30 +00:00
|
|
|
|
|
|
|
/* Wake up pending writers */
|
2019-12-11 01:32:41 +00:00
|
|
|
if (wasAtCapacity) janet_mailbox_wakeup(mailbox);
|
2019-12-08 18:30:30 +00:00
|
|
|
|
2019-12-07 18:14:16 +00:00
|
|
|
return 0;
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
if (wait.nowait || mailbox->refCount <= 1) {
|
|
|
|
janet_mailbox_unlock(mailbox);
|
2019-12-07 18:14:16 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-07 18:14:16 +00:00
|
|
|
/* Wait for next message */
|
2019-12-11 01:32:41 +00:00
|
|
|
if (janet_waiter_wait(&wait, mailbox)) {
|
|
|
|
janet_mailbox_unlock(mailbox);
|
|
|
|
return 1;
|
2019-12-06 07:46:23 +00:00
|
|
|
}
|
2019-12-02 03:53:39 +00:00
|
|
|
}
|
2019-12-06 07:46:23 +00:00
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 10:39:13 +00:00
|
|
|
static Janet janet_thread_getter(void *p, Janet key);
|
|
|
|
|
2019-11-27 05:13:53 +00:00
|
|
|
static JanetAbstractType Thread_AT = {
|
|
|
|
"core/thread",
|
|
|
|
thread_gc,
|
2019-12-02 02:28:12 +00:00
|
|
|
thread_mark,
|
2019-12-02 10:39:13 +00:00
|
|
|
janet_thread_getter,
|
2019-11-27 05:13:53 +00:00
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
static JanetThread *janet_make_thread(JanetMailbox *mailbox, JanetTable *encode) {
|
2019-12-02 02:28:12 +00:00
|
|
|
JanetThread *thread = janet_abstract(&Thread_AT, sizeof(JanetThread));
|
2019-12-07 18:14:16 +00:00
|
|
|
thread->mailbox = mailbox;
|
|
|
|
thread->encode = encode;
|
2019-12-02 02:28:12 +00:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2019-12-05 04:31:01 +00:00
|
|
|
JanetThread *janet_getthread(const Janet *argv, int32_t n) {
|
2019-11-27 05:13:53 +00:00
|
|
|
return (JanetThread *) janet_getabstract(argv, n, &Thread_AT);
|
|
|
|
}
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
static JanetTable *janet_get_core_table(const char *name) {
|
|
|
|
JanetTable *env = janet_core_env(NULL);
|
|
|
|
Janet out = janet_wrap_nil();
|
|
|
|
JanetBindingType bt = janet_resolve(env, janet_csymbol(name), &out);
|
|
|
|
if (bt == JANET_BINDING_NONE) return NULL;
|
|
|
|
if (!janet_checktype(out, JANET_TABLE)) return NULL;
|
|
|
|
return janet_unwrap_table(out);
|
|
|
|
}
|
|
|
|
|
2019-11-27 05:13:53 +00:00
|
|
|
/* Runs in new thread */
|
2019-12-07 18:14:16 +00:00
|
|
|
static int thread_worker(JanetMailbox *mailbox) {
|
2019-12-07 22:51:00 +00:00
|
|
|
JanetFiber *fiber = NULL;
|
|
|
|
Janet out;
|
|
|
|
|
|
|
|
/* Use the mailbox we were given */
|
|
|
|
janet_vm_mailbox = mailbox;
|
|
|
|
|
2019-11-27 05:13:53 +00:00
|
|
|
/* Init VM */
|
|
|
|
janet_init();
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
/* Get dictionaries for default encode/decode */
|
2019-12-02 10:15:22 +00:00
|
|
|
JanetTable *encode = janet_get_core_table("make-image-dict");
|
2019-12-07 22:51:00 +00:00
|
|
|
mailbox->decode = janet_get_core_table("load-image-dict");
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
/* Create parent thread */
|
|
|
|
JanetThread *parent = janet_make_thread(mailbox->parent, encode);
|
|
|
|
janet_mailbox_ref(mailbox->parent, -1);
|
|
|
|
mailbox->parent = NULL; /* only used to create the thread */
|
|
|
|
Janet parentv = janet_wrap_abstract(parent);
|
2019-12-07 18:14:16 +00:00
|
|
|
|
2019-11-27 05:13:53 +00:00
|
|
|
/* Unmarshal the function */
|
2019-12-02 02:28:12 +00:00
|
|
|
Janet funcv;
|
2019-12-07 22:51:00 +00:00
|
|
|
int status = janet_thread_receive(&funcv, -1.0);
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
if (status) goto error;
|
2019-11-27 05:13:53 +00:00
|
|
|
if (!janet_checktype(funcv, JANET_FUNCTION)) goto error;
|
|
|
|
JanetFunction *func = janet_unwrap_function(funcv);
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
/* Arity check */
|
|
|
|
if (func->def->min_arity > 1 || func->def->max_arity < 1) {
|
|
|
|
goto error;
|
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
|
|
|
|
/* Call function */
|
2019-12-07 22:51:00 +00:00
|
|
|
Janet argv[1] = { parentv };
|
|
|
|
fiber = janet_fiber(func, 64, 1, argv);
|
|
|
|
JanetSignal sig = janet_continue(fiber, janet_wrap_nil(), &out);
|
|
|
|
if (sig != JANET_SIGNAL_OK) {
|
|
|
|
janet_eprintf("in thread %v: ", janet_wrap_abstract(janet_make_thread(mailbox, encode)));
|
|
|
|
janet_stacktrace(fiber, out);
|
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
/* Normal exit */
|
2019-11-27 05:13:53 +00:00
|
|
|
janet_deinit();
|
|
|
|
return 0;
|
|
|
|
|
2019-12-07 22:51:00 +00:00
|
|
|
/* Fail to set something up */
|
2019-11-27 05:13:53 +00:00
|
|
|
error:
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_eprintf("\nthread failed to start\n");
|
2019-11-27 05:13:53 +00:00
|
|
|
janet_deinit();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
#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
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
static void *janet_pthread_wrapper(void *param) {
|
2019-12-07 22:51:00 +00:00
|
|
|
thread_worker((JanetMailbox *)param);
|
2019-11-27 05:13:53 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-05 03:04:43 +00:00
|
|
|
static int janet_thread_start_child(JanetThread *thread) {
|
2019-12-02 02:47:22 +00:00
|
|
|
pthread_t handle;
|
2019-12-07 22:51:00 +00:00
|
|
|
int error = pthread_create(&handle, NULL, janet_pthread_wrapper, thread->mailbox);
|
2019-11-27 05:13:53 +00:00
|
|
|
if (error) {
|
2019-12-05 03:04:43 +00:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
pthread_detach(handle);
|
|
|
|
return 0;
|
2019-11-27 05:13:53 +00:00
|
|
|
}
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 01:32:41 +00:00
|
|
|
#endif
|
|
|
|
|
2019-12-07 23:54:08 +00:00
|
|
|
/*
|
|
|
|
* Setup/Teardown
|
|
|
|
*/
|
|
|
|
|
|
|
|
void janet_threads_init(void) {
|
|
|
|
if (NULL == janet_vm_mailbox) {
|
2019-12-10 19:26:00 +00:00
|
|
|
janet_vm_mailbox = janet_mailbox_create(NULL, 1, 10);
|
2019-12-07 23:54:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void janet_threads_deinit(void) {
|
2019-12-11 01:32:41 +00:00
|
|
|
janet_mailbox_lock(janet_vm_mailbox);
|
2019-12-07 23:54:08 +00:00
|
|
|
janet_vm_mailbox->closed = 1;
|
|
|
|
janet_mailbox_ref_with_lock(janet_vm_mailbox, -1);
|
|
|
|
janet_vm_mailbox = NULL;
|
2019-12-08 18:30:30 +00:00
|
|
|
janet_vm_thread_current = NULL;
|
2019-12-07 23:54:08 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
/*
|
|
|
|
* Cfuns
|
|
|
|
*/
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-08 18:30:30 +00:00
|
|
|
static Janet cfun_thread_current(int32_t argc, Janet *argv) {
|
2019-12-07 23:54:08 +00:00
|
|
|
(void) argv;
|
|
|
|
janet_fixarity(argc, 0);
|
2019-12-08 18:30:30 +00:00
|
|
|
if (NULL == janet_vm_thread_current) {
|
|
|
|
janet_vm_thread_current = janet_make_thread(janet_vm_mailbox, janet_get_core_table("make-image-dict"));
|
2019-12-07 23:54:08 +00:00
|
|
|
janet_mailbox_ref(janet_vm_mailbox, 1);
|
2019-12-08 18:30:30 +00:00
|
|
|
janet_gcroot(janet_wrap_abstract(janet_vm_thread_current));
|
2019-12-07 23:54:08 +00:00
|
|
|
}
|
2019-12-08 18:30:30 +00:00
|
|
|
return janet_wrap_abstract(janet_vm_thread_current);
|
2019-12-07 23:54:08 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 10:15:22 +00:00
|
|
|
static Janet cfun_thread_new(int32_t argc, Janet *argv) {
|
2019-12-08 18:30:30 +00:00
|
|
|
janet_arity(argc, 0, 1);
|
2019-12-10 19:26:00 +00:00
|
|
|
int32_t cap = janet_optinteger(argv, argc, 0, 10);
|
|
|
|
if (cap < 1 || cap > UINT16_MAX) {
|
|
|
|
janet_panicf("bad slot #1, expected integer in range [1, 65535], got %d", cap);
|
|
|
|
}
|
2019-12-07 22:51:00 +00:00
|
|
|
JanetTable *encode = janet_get_core_table("make-image-dict");
|
2019-12-10 19:26:00 +00:00
|
|
|
JanetMailbox *mailbox = janet_mailbox_create(janet_vm_mailbox, 2, (uint16_t) cap);
|
2019-12-07 22:51:00 +00:00
|
|
|
|
|
|
|
/* one for created thread, one for ->parent reference in new mailbox */
|
|
|
|
janet_mailbox_ref(janet_vm_mailbox, 2);
|
|
|
|
|
|
|
|
JanetThread *thread = janet_make_thread(mailbox, encode);
|
|
|
|
if (janet_thread_start_child(thread)) {
|
|
|
|
janet_mailbox_ref(mailbox, -1); /* mailbox reference */
|
|
|
|
janet_mailbox_ref(janet_vm_mailbox, -1); /* ->parent reference */
|
2019-12-05 03:04:43 +00:00
|
|
|
janet_panic("could not start thread");
|
2019-12-07 22:51:00 +00:00
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
return janet_wrap_abstract(thread);
|
|
|
|
}
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
static Janet cfun_thread_send(int32_t argc, Janet *argv) {
|
2019-12-08 18:30:30 +00:00
|
|
|
janet_arity(argc, 2, 3);
|
2019-12-07 22:51:00 +00:00
|
|
|
JanetThread *thread = janet_getthread(argv, 0);
|
2019-12-08 18:30:30 +00:00
|
|
|
int status = janet_thread_send(thread, argv[1], janet_optnumber(argv, argc, 2, 1.0));
|
2019-12-07 22:51:00 +00:00
|
|
|
switch (status) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
janet_panicf("failed to send message %v", argv[1]);
|
|
|
|
case 2:
|
|
|
|
janet_panic("thread mailbox is closed");
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
return argv[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_arity(argc, 0, 1);
|
2019-12-08 18:30:30 +00:00
|
|
|
double wait = janet_optnumber(argv, argc, 0, 1.0);
|
2019-12-07 22:51:00 +00:00
|
|
|
Janet out;
|
|
|
|
int status = janet_thread_receive(&out, wait);
|
|
|
|
switch (status) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
janet_panicf("timeout after %f seconds", wait);
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-12-05 03:44:53 +00:00
|
|
|
static Janet cfun_thread_close(int32_t argc, Janet *argv) {
|
|
|
|
janet_fixarity(argc, 1);
|
|
|
|
JanetThread *thread = janet_getthread(argv, 0);
|
|
|
|
janet_close_thread(thread);
|
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
|
2019-12-02 10:39:13 +00:00
|
|
|
static const JanetMethod janet_thread_methods[] = {
|
|
|
|
{"send", cfun_thread_send},
|
2019-12-05 03:44:53 +00:00
|
|
|
{"close", cfun_thread_close},
|
2019-12-02 10:39:13 +00:00
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Janet janet_thread_getter(void *p, Janet key) {
|
|
|
|
(void) p;
|
|
|
|
if (!janet_checktype(key, JANET_KEYWORD)) janet_panicf("expected keyword method");
|
|
|
|
return janet_getmethod(janet_unwrap_keyword(key), janet_thread_methods);
|
|
|
|
}
|
|
|
|
|
2019-11-27 18:43:45 +00:00
|
|
|
static const JanetReg threadlib_cfuns[] = {
|
2019-12-07 23:54:08 +00:00
|
|
|
{
|
2019-12-08 18:30:30 +00:00
|
|
|
"thread/current", cfun_thread_current,
|
|
|
|
JDOC("(thread/current)\n\n"
|
2019-12-07 23:54:08 +00:00
|
|
|
"Get the current running thread.")
|
|
|
|
},
|
2019-11-27 05:13:53 +00:00
|
|
|
{
|
2019-12-02 10:15:22 +00:00
|
|
|
"thread/new", cfun_thread_new,
|
2019-12-08 18:30:30 +00:00
|
|
|
JDOC("(thread/new &opt capacity)\n\n"
|
2019-12-10 19:26:00 +00:00
|
|
|
"Start a new thread. The thread will wait for a message containing the function used to start the thread, which should be passed to the thread "
|
|
|
|
"via thread/send. If capacity is provided, that is how many messages can be stored in the thread's mailbox before blocking senders. "
|
|
|
|
"The capacity must be between 1 and 65535 inclusive, and defaults to 10. "
|
|
|
|
"Returns a handle to the new thread.")
|
2019-12-02 02:28:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"thread/send", cfun_thread_send,
|
|
|
|
JDOC("(thread/send thread msg)\n\n"
|
|
|
|
"Send a message to the thread. This will never block and returns thread immediately. "
|
|
|
|
"Will throw an error if there is a problem sending the message.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"thread/receive", cfun_thread_receive,
|
2019-12-07 22:51:00 +00:00
|
|
|
JDOC("(thread/receive &opt timeout)\n\n"
|
|
|
|
"Get a message sent to this thread. If timeout is provided, an error will be thrown after the timeout has elapsed but "
|
|
|
|
"no messages are received.")
|
2019-11-27 05:13:53 +00:00
|
|
|
},
|
2019-12-05 03:44:53 +00:00
|
|
|
{
|
|
|
|
"thread/close", cfun_thread_close,
|
|
|
|
JDOC("(thread/close thread)\n\n"
|
|
|
|
"Close a thread, unblocking it and ending communication with it. Note that closing "
|
|
|
|
"a thread is idempotent and does not cancel the thread's operation. Returns nil.")
|
|
|
|
},
|
2019-11-27 05:13:53 +00:00
|
|
|
{NULL, NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Module entry point */
|
|
|
|
void janet_lib_thread(JanetTable *env) {
|
2019-11-27 18:43:45 +00:00
|
|
|
janet_core_cfuns(env, NULL, threadlib_cfuns);
|
2019-12-07 22:51:00 +00:00
|
|
|
janet_register_abstract_type(&Thread_AT);
|
2019-11-27 05:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|