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-02 02:28:12 +00:00
|
|
|
#include <setjmp.h>
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
static JANET_THREAD_LOCAL JanetThreadSelector janet_vm_thread_selector;
|
2019-12-05 03:44:53 +00:00
|
|
|
|
|
|
|
void janet_threads_init(void) {
|
2019-12-06 07:46:23 +00:00
|
|
|
pthread_mutex_init(&janet_vm_thread_selector.mutex, NULL);
|
|
|
|
pthread_cond_init(&janet_vm_thread_selector.cond, NULL);
|
|
|
|
janet_vm_thread_selector.channel = NULL;
|
2019-12-05 03:44:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void janet_threads_deinit(void) {
|
2019-12-06 07:46:23 +00:00
|
|
|
pthread_mutex_destroy(&janet_vm_thread_selector.mutex);
|
|
|
|
pthread_cond_destroy(&janet_vm_thread_selector.cond);
|
|
|
|
janet_vm_thread_selector.channel = NULL;
|
2019-12-05 03:44:53 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 10:15:22 +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-12-05 03:04:43 +00:00
|
|
|
static void janet_channel_init(JanetChannel *channel) {
|
|
|
|
janet_buffer_init(&channel->buf, 0);
|
2019-12-02 02:28:12 +00:00
|
|
|
pthread_mutex_init(&channel->lock, NULL);
|
2019-12-06 07:46:23 +00:00
|
|
|
channel->selector = NULL;
|
2019-12-05 03:04:43 +00:00
|
|
|
channel->refCount = 2;
|
2019-12-06 07:46:23 +00:00
|
|
|
channel->encode = NULL;
|
|
|
|
channel->decode = NULL;
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-05 03:44:53 +00:00
|
|
|
/* Return 1 if channel memory should be freed, otherwise 0 */
|
2019-12-05 03:04:43 +00:00
|
|
|
static int janet_channel_deref(JanetChannel *channel) {
|
|
|
|
pthread_mutex_lock(&channel->lock);
|
2019-12-05 03:44:53 +00:00
|
|
|
if (1 == channel->refCount) {
|
2019-12-05 03:04:43 +00:00
|
|
|
janet_buffer_deinit(&channel->buf);
|
|
|
|
pthread_mutex_destroy(&channel->lock);
|
|
|
|
return 1;
|
|
|
|
} else {
|
2019-12-05 03:44:53 +00:00
|
|
|
channel->refCount--;
|
2019-12-05 03:04:43 +00:00
|
|
|
pthread_mutex_unlock(&channel->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
}
|
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
static void janet_close_thread(JanetThread *thread) {
|
|
|
|
if (NULL != thread->rx) {
|
|
|
|
JanetChannel *rx = thread->rx;
|
|
|
|
JanetChannel *tx = thread->tx;
|
|
|
|
/* Deref both. The reference counts should be in sync. */
|
|
|
|
janet_channel_deref(rx);
|
|
|
|
if (janet_channel_deref(tx)) {
|
|
|
|
/* tx and rx were allocated together. free the one with the lower address. */
|
|
|
|
free(rx < tx ? rx : tx);
|
|
|
|
}
|
|
|
|
thread->rx = NULL;
|
|
|
|
thread->tx = NULL;
|
|
|
|
}
|
|
|
|
}
|
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) {
|
|
|
|
JanetThread *thread = (JanetThread *)p;
|
|
|
|
(void) size;
|
|
|
|
JanetChannel *rx = thread->rx;
|
|
|
|
JanetChannel *tx = thread->tx;
|
|
|
|
if (tx && tx->encode) {
|
|
|
|
janet_mark(janet_wrap_table(tx->encode));
|
|
|
|
}
|
|
|
|
if (rx && rx->encode) {
|
|
|
|
janet_mark(janet_wrap_table(rx->decode));
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
2019-12-06 07:46:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if could not send, but do not panic or block (for long). */
|
|
|
|
static int janet_channel_send(JanetChannel *tx, Janet msg) {
|
|
|
|
JanetThreadSelector *selector = tx->selector;
|
|
|
|
|
|
|
|
/* Check for closed channel */
|
|
|
|
if (tx->refCount <= 1) return 1;
|
2019-12-05 03:04:43 +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-06 07:46:23 +00:00
|
|
|
int32_t oldcount = tx->buf.count;
|
2019-12-02 02:28:12 +00:00
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
if (setjmp(buf)) {
|
|
|
|
ret = 1;
|
2019-12-06 07:46:23 +00:00
|
|
|
tx->buf.count = oldcount;
|
2019-12-02 02:28:12 +00:00
|
|
|
} else {
|
2019-12-06 07:46:23 +00:00
|
|
|
janet_marshal(&tx->buf, msg, tx->encode, 0);
|
|
|
|
if (selector) {
|
|
|
|
pthread_mutex_lock(&selector->mutex);
|
|
|
|
if (!selector->channel) {
|
|
|
|
selector->channel = tx;
|
|
|
|
pthread_cond_signal(&selector->cond);
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&selector->mutex);
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
janet_vm_jmp_buf = old_buf;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
/* Returns 0 on successful message.
|
|
|
|
* Returns 1 if nothing in queue or failed to get item. In this case,
|
|
|
|
* also sets the channel's selector value.
|
|
|
|
* Returns 2 if channel closed.
|
|
|
|
* Does not block (for long) or panic, and sets the channel's selector
|
|
|
|
* . */
|
|
|
|
static int janet_channel_receive(JanetChannel *rx, Janet *msg_out) {
|
|
|
|
|
|
|
|
/* Check for no messages */
|
|
|
|
while (rx->buf.count == 0) {
|
|
|
|
int is_dead = rx->refCount <= 1;
|
|
|
|
rx->selector = &janet_vm_thread_selector;
|
|
|
|
return is_dead ? 2 : 1;
|
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;
|
|
|
|
|
|
|
|
/* Handle errors */
|
|
|
|
int ret = 0;
|
|
|
|
if (setjmp(buf)) {
|
2019-12-06 07:46:23 +00:00
|
|
|
rx->buf.count = 0;
|
|
|
|
rx->selector = &janet_vm_thread_selector;
|
2019-12-02 02:28:12 +00:00
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
/* Read from beginning of channel */
|
|
|
|
const uint8_t *nextItem = NULL;
|
2019-12-06 07:46:23 +00:00
|
|
|
Janet item = janet_unmarshal(rx->buf.data, rx->buf.count,
|
|
|
|
0, rx->decode, &nextItem);
|
2019-12-02 02:28:12 +00:00
|
|
|
|
|
|
|
/* Update memory and put result into *msg_out */
|
2019-12-06 07:46:23 +00:00
|
|
|
int32_t chunkCount = nextItem - rx->buf.data;
|
|
|
|
memmove(rx->buf.data, nextItem, rx->buf.count - chunkCount);
|
|
|
|
rx->buf.count -= chunkCount;
|
2019-12-02 02:28:12 +00:00
|
|
|
*msg_out = item;
|
2019-12-06 07:46:23 +00:00
|
|
|
|
|
|
|
/* Got message, unset selector */
|
|
|
|
rx->selector = NULL;
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
janet_vm_jmp_buf = old_buf;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
/* Get a message from one of the channels given. */
|
|
|
|
static int janet_channel_select(int32_t n, JanetChannel **rxs, Janet *msg_out) {
|
|
|
|
int32_t maxChannel = -1;
|
2019-12-05 04:31:01 +00:00
|
|
|
for (;;) {
|
2019-12-06 07:46:23 +00:00
|
|
|
janet_vm_thread_selector.channel = NULL;
|
|
|
|
|
|
|
|
/* Try each channel, first without acquiring locks and looking
|
|
|
|
* only for existing messages, then with acquiring
|
|
|
|
* locks, which will not miss messages. */
|
|
|
|
for (int trylock = 1; trylock >= 0; trylock--) {
|
|
|
|
for (int32_t i = 0; i < n; i++) {
|
|
|
|
JanetChannel *rx = rxs[i];
|
|
|
|
if (trylock) {
|
|
|
|
if (rx->buf.count == 0 || pthread_mutex_trylock(&rx->lock)) continue;
|
|
|
|
} else {
|
|
|
|
pthread_mutex_lock(&rxs[i]->lock);
|
|
|
|
}
|
|
|
|
int status = janet_channel_receive(rxs[i], msg_out);
|
|
|
|
pthread_mutex_unlock(&rxs[i]->lock);
|
|
|
|
if (status == 0) goto gotMessage;
|
|
|
|
maxChannel = maxChannel > i ? maxChannel : i;
|
|
|
|
if (status == 2) {
|
|
|
|
/* channel closed and will receive no more messages, drop it */
|
|
|
|
rxs[i] = rxs[--n];
|
|
|
|
--i;
|
|
|
|
}
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
/* All channels closed */
|
|
|
|
if (n == 0) return 1;
|
2019-12-05 03:04:43 +00:00
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
pthread_mutex_lock(&janet_vm_thread_selector.mutex);
|
|
|
|
{
|
|
|
|
/* Wait until we have a channel */
|
|
|
|
if (NULL == janet_vm_thread_selector.channel) {
|
|
|
|
pthread_cond_wait(
|
|
|
|
&janet_vm_thread_selector.cond,
|
|
|
|
&janet_vm_thread_selector.mutex);
|
|
|
|
}
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-06 07:46:23 +00:00
|
|
|
/* Got channel, swap it with first channel, and
|
|
|
|
* then go back to receiving messages. */
|
|
|
|
JanetChannel *rx = janet_vm_thread_selector.channel;
|
|
|
|
int32_t index = 0;
|
|
|
|
while (rxs[index] != rx) index++;
|
|
|
|
rxs[index] = rxs[0];
|
|
|
|
rxs[0] = rx;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&janet_vm_thread_selector.mutex);
|
2019-12-02 03:53:39 +00:00
|
|
|
}
|
2019-12-06 07:46:23 +00:00
|
|
|
|
|
|
|
gotMessage:
|
|
|
|
/* got message, unset selectors and return */
|
|
|
|
for (int32_t j = 0; j <= maxChannel && j < n; j++) {
|
|
|
|
pthread_mutex_lock(&rxs[j]->lock);
|
|
|
|
rxs[j]->selector = NULL;
|
|
|
|
pthread_mutex_unlock(&rxs[j]->lock);
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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-05 03:04:43 +00:00
|
|
|
static JanetThread *janet_make_thread(JanetChannel *rx, JanetChannel *tx, JanetTable *encode, JanetTable *decode) {
|
2019-12-02 02:28:12 +00:00
|
|
|
JanetThread *thread = janet_abstract(&Thread_AT, sizeof(JanetThread));
|
2019-12-05 03:04:43 +00:00
|
|
|
thread->rx = rx;
|
|
|
|
thread->tx = tx;
|
2019-12-06 07:46:23 +00:00
|
|
|
rx->decode = decode;
|
|
|
|
tx->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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Runs in new thread */
|
2019-12-05 03:04:43 +00:00
|
|
|
static int thread_worker(JanetChannel *tx) {
|
2019-11-27 05:13:53 +00:00
|
|
|
/* Init VM */
|
|
|
|
janet_init();
|
|
|
|
|
2019-12-02 03:53:39 +00:00
|
|
|
/* Get dictionaries */
|
2019-12-02 10:15:22 +00:00
|
|
|
JanetTable *decode = janet_get_core_table("load-image-dict");
|
|
|
|
JanetTable *encode = janet_get_core_table("make-image-dict");
|
2019-12-02 02:28:12 +00:00
|
|
|
|
|
|
|
/* Create self thread */
|
2019-12-05 03:04:43 +00:00
|
|
|
JanetChannel *rx = tx + 1;
|
|
|
|
JanetThread *thread = janet_make_thread(rx, tx, encode, decode);
|
2019-12-02 02:28:12 +00:00
|
|
|
Janet threadv = janet_wrap_abstract(thread);
|
2019-11-27 05:13:53 +00:00
|
|
|
|
|
|
|
/* Unmarshal the function */
|
2019-12-02 02:28:12 +00:00
|
|
|
Janet funcv;
|
2019-12-06 07:46:23 +00:00
|
|
|
int status = janet_channel_select(1, &rx, &funcv);
|
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-02 02:28:12 +00:00
|
|
|
Janet argv[1] = { threadv };
|
|
|
|
JanetFiber *fiber = janet_fiber(func, 64, 1, argv);
|
2019-11-27 05:13:53 +00:00
|
|
|
Janet out;
|
|
|
|
janet_continue(fiber, janet_wrap_nil(), &out);
|
|
|
|
|
|
|
|
/* Success */
|
|
|
|
janet_deinit();
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Fail */
|
|
|
|
error:
|
|
|
|
janet_deinit();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-12-02 02:28:12 +00:00
|
|
|
static void *janet_pthread_wrapper(void *param) {
|
2019-12-05 03:04:43 +00:00
|
|
|
thread_worker((JanetChannel *)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-05 03:04:43 +00:00
|
|
|
/* My rx is your tx and vice versa */
|
|
|
|
int error = pthread_create(&handle, NULL, janet_pthread_wrapper, thread->rx);
|
2019-11-27 05:13:53 +00:00
|
|
|
if (error) {
|
2019-12-05 03:04:43 +00:00
|
|
|
/* double close as there is no other side to close thread */
|
|
|
|
janet_close_thread(thread);
|
|
|
|
janet_close_thread(thread);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
pthread_detach(handle);
|
|
|
|
return 0;
|
2019-11-27 05:13:53 +00:00
|
|
|
}
|
2019-12-02 02:28:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cfuns
|
|
|
|
*/
|
2019-11-27 05:13:53 +00:00
|
|
|
|
2019-12-02 10:15:22 +00:00
|
|
|
static Janet cfun_thread_new(int32_t argc, Janet *argv) {
|
|
|
|
janet_arity(argc, 0, 2);
|
|
|
|
JanetTable *encode = (argc < 1 || janet_checktype(argv[0], JANET_NIL))
|
|
|
|
? janet_get_core_table("make-image-dict")
|
|
|
|
: janet_gettable(argv, 0);
|
|
|
|
JanetTable *decode = (argc < 2 || janet_checktype(argv[1], JANET_NIL))
|
|
|
|
? janet_get_core_table("load-image-dict")
|
|
|
|
: janet_gettable(argv, 1);
|
2019-12-05 03:04:43 +00:00
|
|
|
JanetChannel *rx = malloc(2 * sizeof(JanetChannel));
|
|
|
|
if (NULL == rx) {
|
|
|
|
JANET_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
JanetChannel *tx = rx + 1;
|
|
|
|
janet_channel_init(rx);
|
|
|
|
janet_channel_init(tx);
|
|
|
|
JanetThread *thread = janet_make_thread(rx, tx, encode, decode);
|
|
|
|
if (janet_thread_start_child(thread))
|
|
|
|
janet_panic("could not start thread");
|
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) {
|
|
|
|
janet_fixarity(argc, 2);
|
2019-12-06 07:46:23 +00:00
|
|
|
JanetChannel *tx = janet_getthread(argv, 0)->tx;
|
|
|
|
if (NULL == tx) janet_panic("channel has closed");
|
|
|
|
pthread_mutex_lock(&tx->lock);
|
|
|
|
int status = janet_channel_send(tx, argv[1]);
|
|
|
|
pthread_mutex_unlock(&tx->lock);
|
2019-12-02 02:28:12 +00:00
|
|
|
if (status) {
|
|
|
|
janet_panicf("failed to send message %v", argv[1]);
|
|
|
|
}
|
|
|
|
return argv[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
|
|
|
|
janet_fixarity(argc, 1);
|
2019-12-05 04:31:01 +00:00
|
|
|
int status;
|
2019-12-02 02:28:12 +00:00
|
|
|
Janet out = janet_wrap_nil();
|
2019-12-05 04:31:01 +00:00
|
|
|
int32_t count;
|
|
|
|
const Janet *items;
|
|
|
|
if (janet_indexed_view(argv[0], &items, &count)) {
|
2019-12-06 07:46:23 +00:00
|
|
|
/* Select on multiple threads */
|
|
|
|
if (count == 0) janet_panic("expected at least 1 thread");
|
|
|
|
int32_t realcount = 0;
|
|
|
|
JanetChannel *rxs_stack[10] = {NULL};
|
|
|
|
JanetChannel **rxs = (count > 10)
|
|
|
|
? janet_smalloc(count * sizeof(JanetChannel *))
|
|
|
|
: rxs_stack;
|
|
|
|
for (int32_t i = 0; i < count; i++) {
|
|
|
|
JanetThread *thread = janet_getthread(items, i);
|
|
|
|
if (thread->rx != NULL) rxs[realcount++] = thread->rx;
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
2019-12-06 07:46:23 +00:00
|
|
|
status = janet_channel_select(realcount, rxs, &out);
|
|
|
|
if (rxs != rxs_stack) janet_sfree(rxs);
|
2019-12-05 04:31:01 +00:00
|
|
|
} else {
|
|
|
|
/* Get from one thread */
|
|
|
|
JanetThread *thread = janet_getthread(argv, 0);
|
|
|
|
if (NULL == thread->rx) janet_panic("channel has closed");
|
2019-12-06 07:46:23 +00:00
|
|
|
status = janet_channel_select(1, &thread->rx, &out);
|
2019-12-05 04:31:01 +00:00
|
|
|
}
|
2019-12-02 02:28:12 +00:00
|
|
|
if (status) {
|
|
|
|
janet_panic("failed to receive message");
|
|
|
|
}
|
|
|
|
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},
|
|
|
|
{"receive", cfun_thread_receive},
|
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-11-27 05:13:53 +00:00
|
|
|
{
|
2019-12-02 10:15:22 +00:00
|
|
|
"thread/new", cfun_thread_new,
|
|
|
|
JDOC("(thread/new &opt encode-book decode-book)\n\n"
|
2019-12-02 02:28:12 +00:00
|
|
|
"Start a new thread. The thread will wait for a message containing the function used to start the thread, which should be subsequently "
|
|
|
|
"sent over after thread creation.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"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-05 04:31:01 +00:00
|
|
|
JDOC("(thread/receive threads)\n\n"
|
2019-12-05 04:39:30 +00:00
|
|
|
"Get a value sent to 1 or more threads. Will block if no value was sent to this thread "
|
2019-12-05 04:31:01 +00:00
|
|
|
"yet. threads can also be an array or tuple of threads, in which case "
|
|
|
|
"thread/receive will select on the first thread to return a value. Returns "
|
|
|
|
"the message sent to the thread.")
|
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-11-27 05:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|