mirror of
https://github.com/janet-lang/janet
synced 2025-07-06 12:02:53 +00:00
Simplify structure JanetThread and JanetChannel.
Remove JanetThreadShared.
This commit is contained in:
parent
a1f35e21c7
commit
de6c3d6d70
@ -4,7 +4,7 @@
|
|||||||
(def interval (:receive parent))
|
(def interval (:receive parent))
|
||||||
(for i 0 10
|
(for i 0 10
|
||||||
(os/sleep interval)
|
(os/sleep interval)
|
||||||
(printf "thread %s wakeup no. %d\n" name i))
|
(printf "thread %s wakeup no. %d" name i))
|
||||||
(:send parent :done))
|
(:send parent :done))
|
||||||
|
|
||||||
(defn make-worker
|
(defn make-worker
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
(def bob (make-worker "bob" 0.2))
|
(def bob (make-worker "bob" 0.2))
|
||||||
(def joe (make-worker "joe" 0.3))
|
(def joe (make-worker "joe" 0.3))
|
||||||
(def sam (make-worker "joe" 0.5))
|
(def sam (make-worker "sam" 0.5))
|
||||||
|
|
||||||
# Receive out of order
|
# Receive out of order
|
||||||
(:receive bob)
|
(:receive bob)
|
||||||
|
@ -41,43 +41,35 @@ static JanetTable *janet_get_core_table(const char *name) {
|
|||||||
return janet_unwrap_table(out);
|
return janet_unwrap_table(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void janet_channel_init(JanetChannel *channel, size_t initialSize) {
|
static void janet_channel_init(JanetChannel *channel) {
|
||||||
janet_buffer_init(&channel->buf, (int32_t) initialSize);
|
janet_buffer_init(&channel->buf, 0);
|
||||||
pthread_mutex_init(&channel->lock, NULL);
|
pthread_mutex_init(&channel->lock, NULL);
|
||||||
pthread_cond_init(&channel->cond, NULL);
|
pthread_cond_init(&channel->cond, NULL);
|
||||||
|
channel->refCount = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void janet_channel_destroy(JanetChannel *channel) {
|
/* Return 1 if channel memory should be freed, otherwise false */
|
||||||
janet_buffer_deinit(&channel->buf);
|
static int janet_channel_deref(JanetChannel *channel) {
|
||||||
pthread_mutex_destroy(&channel->lock);
|
pthread_mutex_lock(&channel->lock);
|
||||||
pthread_cond_destroy(&channel->cond);
|
if (0 == --channel->refCount) {
|
||||||
}
|
janet_buffer_deinit(&channel->buf);
|
||||||
|
pthread_mutex_destroy(&channel->lock);
|
||||||
static JanetThreadShared *janet_shared_create(size_t initialSize) {
|
pthread_cond_destroy(&channel->cond);
|
||||||
const char *errmsg = "could not allocate memory for thread";
|
return 1;
|
||||||
JanetThreadShared *shared = malloc(sizeof(JanetThreadShared));
|
} else {
|
||||||
if (NULL == shared) janet_panicf(errmsg);
|
pthread_mutex_unlock(&channel->lock);
|
||||||
uint8_t *mem = malloc(initialSize);
|
return 0;
|
||||||
if (NULL == mem) janet_panic(errmsg);
|
}
|
||||||
janet_channel_init(&shared->parent, 0);
|
|
||||||
janet_channel_init(&shared->child, initialSize);
|
|
||||||
shared->refCount = 2;
|
|
||||||
pthread_mutex_init(&shared->refCountLock, NULL);
|
|
||||||
return shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void janet_shared_destroy(JanetThreadShared *shared) {
|
|
||||||
janet_channel_destroy(&shared->parent);
|
|
||||||
janet_channel_destroy(&shared->child);
|
|
||||||
pthread_mutex_destroy(&shared->refCountLock);
|
|
||||||
free(shared);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if could not send. Does not block or panic. Bytes should be a janet value that
|
/* Returns 1 if could not send. Does not block or panic. Bytes should be a janet value that
|
||||||
* has been marshalled. */
|
* has been marshalled. */
|
||||||
static int janet_channel_send_any(JanetChannel *channel, Janet msg, JanetByteView bytes, int is_bytes, JanetTable *dict) {
|
static int janet_channel_send(JanetChannel *channel, Janet msg, JanetTable *dict) {
|
||||||
pthread_mutex_lock(&channel->lock);
|
pthread_mutex_lock(&channel->lock);
|
||||||
|
|
||||||
|
/* Check for closed channel */
|
||||||
|
if (channel->refCount <= 1) return 1;
|
||||||
|
|
||||||
/* Hack to capture all panics from marshalling. This works because
|
/* Hack to capture all panics from marshalling. This works because
|
||||||
* we know janet_marshal won't mess with other essential global state. */
|
* we know janet_marshal won't mess with other essential global state. */
|
||||||
jmp_buf buf;
|
jmp_buf buf;
|
||||||
@ -90,11 +82,7 @@ static int janet_channel_send_any(JanetChannel *channel, Janet msg, JanetByteVie
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
channel->buf.count = oldcount;
|
channel->buf.count = oldcount;
|
||||||
} else {
|
} else {
|
||||||
if (is_bytes) {
|
janet_marshal(&channel->buf, msg, dict, 0);
|
||||||
janet_buffer_push_bytes(&channel->buf, bytes.bytes, bytes.len);
|
|
||||||
} else {
|
|
||||||
janet_marshal(&channel->buf, msg, dict, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Was empty, signal to cond */
|
/* Was empty, signal to cond */
|
||||||
if (oldcount == 0) {
|
if (oldcount == 0) {
|
||||||
@ -109,17 +97,6 @@ static int janet_channel_send_any(JanetChannel *channel, Janet msg, JanetByteVie
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int janet_channel_send(JanetChannel *channel, Janet msg, JanetTable *dict) {
|
|
||||||
JanetByteView dud = {0};
|
|
||||||
return janet_channel_send_any(channel, msg, dud, 0, dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
static int janet_channel_send_image(JanetChannel *channel, JanetByteView bytes) {
|
|
||||||
return janet_channel_send_any(channel, janet_wrap_nil(), bytes, 1, NULL);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Returns 1 if nothing in queue or failed to get item. Does not block or panic. Uses dict to read bytes from
|
/* Returns 1 if nothing in queue or failed to get item. Does not block or panic. Uses dict to read bytes from
|
||||||
* the channel and unmarshal them. */
|
* the channel and unmarshal them. */
|
||||||
static int janet_channel_receive(JanetChannel *channel, Janet *msg_out, JanetTable *dict) {
|
static int janet_channel_receive(JanetChannel *channel, Janet *msg_out, JanetTable *dict) {
|
||||||
@ -127,6 +104,8 @@ static int janet_channel_receive(JanetChannel *channel, Janet *msg_out, JanetTab
|
|||||||
|
|
||||||
/* If queue is empty, block for now. */
|
/* If queue is empty, block for now. */
|
||||||
while (channel->buf.count == 0) {
|
while (channel->buf.count == 0) {
|
||||||
|
/* Check for closed channel (1 ref left means other side quit) */
|
||||||
|
if (channel->refCount <= 1) return 1;
|
||||||
pthread_cond_wait(&channel->cond, &channel->lock);
|
pthread_cond_wait(&channel->cond, &channel->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,18 +140,25 @@ static int janet_channel_receive(JanetChannel *channel, Janet *msg_out, JanetTab
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int thread_gc(void *p, size_t size) {
|
static void janet_close_thread(JanetThread *thread) {
|
||||||
JanetThread *thread = (JanetThread *)p;
|
if (NULL != thread->rx) {
|
||||||
JanetThreadShared *shared = thread->shared;
|
JanetChannel *rx = thread->rx;
|
||||||
if (NULL == shared) return 0;
|
JanetChannel *tx = thread->tx;
|
||||||
(void) size;
|
/* Deref both. The reference counts should be in sync. */
|
||||||
pthread_mutex_lock(&shared->refCountLock);
|
janet_channel_deref(rx);
|
||||||
int refcount = --shared->refCount;
|
if (janet_channel_deref(tx)) {
|
||||||
if (refcount == 0) {
|
/* tx and rx were allocated together. free the one with the lower address. */
|
||||||
janet_shared_destroy(shared);
|
free(rx < tx ? rx : tx);
|
||||||
} else {
|
}
|
||||||
pthread_mutex_unlock(&shared->refCountLock);
|
thread->rx = NULL;
|
||||||
|
thread->tx = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int thread_gc(void *p, size_t size) {
|
||||||
|
(void) size;
|
||||||
|
JanetThread *thread = (JanetThread *)p;
|
||||||
|
janet_close_thread(thread);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,10 +187,10 @@ static JanetAbstractType Thread_AT = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static JanetThread *janet_make_thread(JanetThreadShared *shared, JanetTable *encode, JanetTable *decode, int who) {
|
static JanetThread *janet_make_thread(JanetChannel *rx, JanetChannel *tx, JanetTable *encode, JanetTable *decode) {
|
||||||
JanetThread *thread = janet_abstract(&Thread_AT, sizeof(JanetThread));
|
JanetThread *thread = janet_abstract(&Thread_AT, sizeof(JanetThread));
|
||||||
thread->shared = shared;
|
thread->rx = rx;
|
||||||
thread->kind = who;
|
thread->tx = tx;
|
||||||
thread->encode = encode;
|
thread->encode = encode;
|
||||||
thread->decode = decode;
|
thread->decode = decode;
|
||||||
return thread;
|
return thread;
|
||||||
@ -215,10 +201,7 @@ JanetThread *janet_getthread(Janet *argv, int32_t n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Runs in new thread */
|
/* Runs in new thread */
|
||||||
static int thread_worker(JanetThreadShared *shared) {
|
static int thread_worker(JanetChannel *tx) {
|
||||||
pthread_t handle = pthread_self();
|
|
||||||
pthread_detach(handle);
|
|
||||||
|
|
||||||
/* Init VM */
|
/* Init VM */
|
||||||
janet_init();
|
janet_init();
|
||||||
|
|
||||||
@ -227,12 +210,13 @@ static int thread_worker(JanetThreadShared *shared) {
|
|||||||
JanetTable *encode = janet_get_core_table("make-image-dict");
|
JanetTable *encode = janet_get_core_table("make-image-dict");
|
||||||
|
|
||||||
/* Create self thread */
|
/* Create self thread */
|
||||||
JanetThread *thread = janet_make_thread(shared, encode, decode, JANET_THREAD_SELF);
|
JanetChannel *rx = tx + 1;
|
||||||
|
JanetThread *thread = janet_make_thread(rx, tx, encode, decode);
|
||||||
Janet threadv = janet_wrap_abstract(thread);
|
Janet threadv = janet_wrap_abstract(thread);
|
||||||
|
|
||||||
/* Unmarshal the function */
|
/* Unmarshal the function */
|
||||||
Janet funcv;
|
Janet funcv;
|
||||||
int status = janet_channel_receive(&shared->child, &funcv, decode);
|
int status = janet_channel_receive(rx, &funcv, decode);
|
||||||
if (status) goto error;
|
if (status) goto error;
|
||||||
if (!janet_checktype(funcv, JANET_FUNCTION)) goto error;
|
if (!janet_checktype(funcv, JANET_FUNCTION)) goto error;
|
||||||
JanetFunction *func = janet_unwrap_function(funcv);
|
JanetFunction *func = janet_unwrap_function(funcv);
|
||||||
@ -259,17 +243,22 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void *janet_pthread_wrapper(void *param) {
|
static void *janet_pthread_wrapper(void *param) {
|
||||||
thread_worker((JanetThreadShared *)param);
|
thread_worker((JanetChannel *)param);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void janet_thread_start_child(JanetThread *thread) {
|
static int janet_thread_start_child(JanetThread *thread) {
|
||||||
JanetThreadShared *shared = thread->shared;
|
|
||||||
pthread_t handle;
|
pthread_t handle;
|
||||||
int error = pthread_create(&handle, NULL, janet_pthread_wrapper, shared);
|
/* My rx is your tx and vice versa */
|
||||||
|
int error = pthread_create(&handle, NULL, janet_pthread_wrapper, thread->rx);
|
||||||
if (error) {
|
if (error) {
|
||||||
thread->shared = NULL; /* Prevent GC from trying to mess with shared memory here */
|
/* double close as there is no other side to close thread */
|
||||||
janet_shared_destroy(shared);
|
janet_close_thread(thread);
|
||||||
|
janet_close_thread(thread);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
pthread_detach(handle);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,20 +274,24 @@ static Janet cfun_thread_new(int32_t argc, Janet *argv) {
|
|||||||
JanetTable *decode = (argc < 2 || janet_checktype(argv[1], JANET_NIL))
|
JanetTable *decode = (argc < 2 || janet_checktype(argv[1], JANET_NIL))
|
||||||
? janet_get_core_table("load-image-dict")
|
? janet_get_core_table("load-image-dict")
|
||||||
: janet_gettable(argv, 1);
|
: janet_gettable(argv, 1);
|
||||||
JanetThreadShared *shared = janet_shared_create(0);
|
JanetChannel *rx = malloc(2 * sizeof(JanetChannel));
|
||||||
JanetThread *thread = janet_make_thread(shared, encode, decode, JANET_THREAD_OTHER);
|
if (NULL == rx) {
|
||||||
janet_thread_start_child(thread);
|
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");
|
||||||
return janet_wrap_abstract(thread);
|
return janet_wrap_abstract(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Janet cfun_thread_send(int32_t argc, Janet *argv) {
|
static Janet cfun_thread_send(int32_t argc, Janet *argv) {
|
||||||
janet_fixarity(argc, 2);
|
janet_fixarity(argc, 2);
|
||||||
JanetThread *thread = janet_getthread(argv, 0);
|
JanetThread *thread = janet_getthread(argv, 0);
|
||||||
JanetThreadShared *shared = thread->shared;
|
if (NULL == thread->tx) janet_panic("channel has closed");
|
||||||
if (NULL == shared) janet_panic("channel has closed");
|
int status = janet_channel_send(thread->tx, argv[1], thread->encode);
|
||||||
int status = janet_channel_send(thread->kind == JANET_THREAD_SELF ? &shared->parent : &shared->child,
|
|
||||||
argv[1],
|
|
||||||
thread->encode);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
janet_panicf("failed to send message %v", argv[1]);
|
janet_panicf("failed to send message %v", argv[1]);
|
||||||
}
|
}
|
||||||
@ -308,12 +301,9 @@ static Janet cfun_thread_send(int32_t argc, Janet *argv) {
|
|||||||
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
|
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
JanetThread *thread = janet_getthread(argv, 0);
|
JanetThread *thread = janet_getthread(argv, 0);
|
||||||
JanetThreadShared *shared = thread->shared;
|
if (NULL == thread->rx) janet_panic("channel has closed");
|
||||||
if (NULL == shared) janet_panic("channel has closed");
|
|
||||||
Janet out = janet_wrap_nil();
|
Janet out = janet_wrap_nil();
|
||||||
int status = janet_channel_receive(thread->kind == JANET_THREAD_SELF ? &shared->child : &shared->parent,
|
int status = janet_channel_receive(thread->rx, &out, thread->decode);
|
||||||
&out,
|
|
||||||
thread->decode);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
janet_panic("failed to receive message");
|
janet_panic("failed to receive message");
|
||||||
}
|
}
|
||||||
|
@ -942,27 +942,18 @@ struct JanetRNG {
|
|||||||
#ifdef JANET_THREADS
|
#ifdef JANET_THREADS
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
typedef struct JanetThread JanetThread;
|
typedef struct JanetThread JanetThread;
|
||||||
typedef struct JanetThreadShared JanetThreadShared;
|
|
||||||
typedef struct JanetChannel JanetChannel;
|
typedef struct JanetChannel JanetChannel;
|
||||||
struct JanetChannel {
|
struct JanetChannel {
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
JanetBuffer buf;
|
JanetBuffer buf;
|
||||||
};
|
|
||||||
struct JanetThreadShared {
|
|
||||||
pthread_mutex_t refCountLock;
|
|
||||||
int refCount;
|
int refCount;
|
||||||
JanetChannel parent;
|
|
||||||
JanetChannel child;
|
|
||||||
};
|
};
|
||||||
struct JanetThread {
|
struct JanetThread {
|
||||||
JanetThreadShared *shared;
|
JanetChannel *rx;
|
||||||
|
JanetChannel *tx;
|
||||||
JanetTable *encode;
|
JanetTable *encode;
|
||||||
JanetTable *decode;
|
JanetTable *decode;
|
||||||
enum {
|
|
||||||
JANET_THREAD_SELF,
|
|
||||||
JANET_THREAD_OTHER
|
|
||||||
} kind;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user