2019-11-27 05:13:53 +00:00
/*
2020-01-12 16:50:37 +00:00
* Copyright ( c ) 2020 Calvin Rose
2019-11-27 05:13:53 +00:00
*
* 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
2019-12-31 00:06:15 +00:00
# include "features.h"
2019-11-27 05:13:53 +00:00
# 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-13 01:04:13 +00:00
# include <math.h>
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
/* 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 ;
2019-12-12 08:19:56 +00:00
/* Buffers to store messages. These buffers are manually allocated, so
* are not owned by any thread ' s GC . */
2019-12-10 19:26:00 +00:00
JanetBuffer messages [ ] ;
2019-12-07 18:14:16 +00:00
} ;
2019-11-27 05:13:53 +00:00
2020-06-13 14:42:16 +00:00
# define JANET_THREAD_HEAVYWEIGHT 0x1
# define JANET_THREAD_ABSTRACTS 0x2
# define JANET_THREAD_CFUNCTIONS 0x4
static const char janet_thread_flags [ ] = " hac " ;
2020-02-27 23:58:17 +00:00
typedef struct {
JanetMailbox * original ;
JanetMailbox * newbox ;
2020-06-14 19:20:38 +00:00
uint64_t flags ;
2020-02-27 23:58:17 +00:00
} JanetMailboxPair ;
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 ;
2020-01-16 01:58:14 +00:00
static JANET_THREAD_LOCAL JanetTable * janet_vm_thread_decode = NULL ;
static JanetTable * janet_thread_get_decode ( void ) {
if ( janet_vm_thread_decode = = NULL ) {
janet_vm_thread_decode = janet_get_core_table ( " load-image-dict " ) ;
janet_gcroot ( janet_wrap_table ( janet_vm_thread_decode ) ) ;
}
return janet_vm_thread_decode ;
}
2019-12-05 03:44:53 +00:00
2020-02-27 23:58:17 +00:00
static JanetMailbox * janet_mailbox_create ( int refCount , uint16_t capacity ) {
2020-01-03 04:02:57 +00:00
JanetMailbox * mailbox = malloc ( sizeof ( JanetMailbox ) + sizeof ( JanetBuffer ) * ( size_t ) 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 ;
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 ;
}
2020-06-14 19:20:38 +00:00
static JanetMailboxPair * make_mailbox_pair ( JanetMailbox * original , uint64_t flags ) {
2020-02-27 23:58:17 +00:00
JanetMailboxPair * pair = malloc ( sizeof ( JanetMailboxPair ) ) ;
if ( NULL = = pair ) {
JANET_OUT_OF_MEMORY ;
}
pair - > original = original ;
janet_mailbox_ref ( original , 1 ) ;
pair - > newbox = janet_mailbox_create ( 1 , 16 ) ;
2020-06-13 14:42:16 +00:00
pair - > flags = flags ;
2020-02-27 23:58:17 +00:00
return pair ;
}
2020-02-28 15:04:28 +00:00
static void destroy_mailbox_pair ( JanetMailboxPair * pair ) {
2020-02-27 23:58:17 +00:00
janet_mailbox_ref ( pair - > original , - 1 ) ;
janet_mailbox_ref ( pair - > newbox , - 1 ) ;
free ( pair ) ;
}
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 ;
2019-12-18 21:07:06 +00:00
if ( sec < = 0.0 | | isnan ( sec ) ) {
2019-12-11 01:32:41 +00:00
waiter - > nowait = 1 ;
return ;
}
2019-12-18 21:07:06 +00:00
waiter - > timedwait = sec > 0.0 & & ! isinf ( sec ) ;
2019-12-11 01:32:41 +00:00
/* 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 ;
2020-07-03 14:54:58 +00:00
janet_gettime ( & now ) ;
2019-12-11 01:32:41 +00:00
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-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 ) ) {
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 */
2020-04-19 15:55:00 +00:00
janet_marshal ( msgbuf , msg , thread - > encode , JANET_MARSHAL_UNSAFE ) ;
2019-12-10 19:26:00 +00:00
/* 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-12 08:19:56 +00:00
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 ) {
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 ) ) {
2020-07-04 00:54:58 +00:00
/* Cleanup jmp_buf, return error.
* Do not ignore bad messages as before . */
2019-12-07 18:14:16 +00:00
janet_vm_jmp_buf = old_buf ;
2020-07-04 00:54:58 +00:00
* msg_out = * janet_vm_return_reg ;
janet_mailbox_unlock ( mailbox ) ;
return 2 ;
2019-12-07 18:14:16 +00:00
} 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 ,
2020-04-19 15:55:00 +00:00
JANET_MARSHAL_UNSAFE , janet_thread_get_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
2019-12-12 08:19:56 +00:00
/* Potentially wake up pending threads */
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-18 21:07:06 +00:00
if ( wait . nowait ) {
2019-12-11 01:32:41 +00:00
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-02 02:28:12 +00:00
}
2019-12-12 23:14:36 +00:00
static int janet_thread_getter ( void * p , Janet key , Janet * out ) ;
2019-12-02 10:39:13 +00:00
2020-03-14 15:12:47 +00:00
const JanetAbstractType janet_thread_type = {
2019-11-27 05:13:53 +00:00
" 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 ,
2020-01-20 19:06:50 +00:00
JANET_ATEND_GET
2019-11-27 05:13:53 +00:00
} ;
2019-12-07 22:51:00 +00:00
static JanetThread * janet_make_thread ( JanetMailbox * mailbox , JanetTable * encode ) {
2020-03-14 15:12:47 +00:00
JanetThread * thread = janet_abstract ( & janet_thread_type , sizeof ( JanetThread ) ) ;
2020-02-27 23:58:17 +00:00
janet_mailbox_ref ( mailbox , 1 ) ;
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 ) {
2020-03-14 15:12:47 +00:00
return ( JanetThread * ) janet_getabstract ( argv , n , & janet_thread_type ) ;
2019-11-27 05:13:53 +00:00
}
/* Runs in new thread */
2020-02-27 23:58:17 +00:00
static int thread_worker ( JanetMailboxPair * pair ) {
2019-12-07 22:51:00 +00:00
JanetFiber * fiber = NULL ;
Janet out ;
/* Use the mailbox we were given */
2020-02-27 23:58:17 +00:00
janet_vm_mailbox = pair - > newbox ;
janet_mailbox_ref ( pair - > newbox , 1 ) ;
2019-12-07 22:51:00 +00:00
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 */
2020-06-13 14:42:16 +00:00
JanetTable * encode ;
if ( pair - > flags & JANET_THREAD_HEAVYWEIGHT ) {
encode = janet_get_core_table ( " make-image-dict " ) ;
} else {
encode = NULL ;
janet_vm_thread_decode = janet_table ( 0 ) ;
janet_gcroot ( janet_wrap_table ( janet_vm_thread_decode ) ) ;
}
2019-11-27 05:13:53 +00:00
2019-12-07 22:51:00 +00:00
/* Create parent thread */
2020-02-27 23:58:17 +00:00
JanetThread * parent = janet_make_thread ( pair - > original , encode ) ;
2019-12-07 22:51:00 +00:00
Janet parentv = janet_wrap_abstract ( parent ) ;
2019-12-07 18:14:16 +00:00
2020-06-13 14:42:16 +00:00
/* Unmarshal the abstract registry */
if ( pair - > flags & JANET_THREAD_ABSTRACTS ) {
Janet reg ;
int status = janet_thread_receive ( & reg , INFINITY ) ;
if ( status ) goto error ;
if ( ! janet_checktype ( reg , JANET_TABLE ) ) goto error ;
janet_gcunroot ( janet_wrap_table ( janet_vm_abstract_registry ) ) ;
janet_vm_abstract_registry = janet_unwrap_table ( reg ) ;
janet_gcroot ( janet_wrap_table ( janet_vm_abstract_registry ) ) ;
}
/* Unmarshal the normal registry */
if ( pair - > flags & JANET_THREAD_CFUNCTIONS ) {
Janet reg ;
int status = janet_thread_receive ( & reg , INFINITY ) ;
if ( status ) goto error ;
if ( ! janet_checktype ( reg , JANET_TABLE ) ) goto error ;
janet_gcunroot ( janet_wrap_table ( janet_vm_registry ) ) ;
janet_vm_registry = janet_unwrap_table ( reg ) ;
janet_gcroot ( janet_wrap_table ( janet_vm_registry ) ) ;
}
2019-11-27 05:13:53 +00:00
/* Unmarshal the function */
2019-12-02 02:28:12 +00:00
Janet funcv ;
2019-12-18 21:07:06 +00:00
int status = janet_thread_receive ( & funcv , INFINITY ) ;
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 ) ;
2020-07-03 20:20:19 +00:00
if ( pair - > flags & JANET_THREAD_HEAVYWEIGHT ) {
fiber - > env = janet_table ( 0 ) ;
fiber - > env - > proto = janet_core_env ( NULL ) ;
}
2019-12-07 22:51:00 +00:00
JanetSignal sig = janet_continue ( fiber , janet_wrap_nil ( ) , & out ) ;
2020-02-10 01:04:34 +00:00
if ( sig ! = JANET_SIGNAL_OK & & sig < JANET_SIGNAL_USER0 ) {
2020-02-27 23:58:17 +00:00
janet_eprintf ( " in thread %v: " , janet_wrap_abstract ( janet_make_thread ( pair - > newbox , encode ) ) ) ;
2019-12-07 22:51:00 +00:00
janet_stacktrace ( fiber , out ) ;
}
2019-11-27 05:13:53 +00:00
2020-02-10 01:04:34 +00:00
# ifdef JANET_NET
janet_loop ( ) ;
# endif
2019-12-07 22:51:00 +00:00
/* Normal exit */
2020-02-28 15:04:28 +00:00
destroy_mailbox_pair ( pair ) ;
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 :
2020-02-28 15:04:28 +00:00
destroy_mailbox_pair ( pair ) ;
2019-12-11 01:32:41 +00:00
janet_eprintf ( " \n thread 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
2019-12-13 01:35:40 +00:00
static DWORD WINAPI janet_create_thread_wrapper ( LPVOID param ) {
2020-02-27 23:58:17 +00:00
thread_worker ( ( JanetMailboxPair * ) param ) ;
2019-12-11 01:32:41 +00:00
return 0 ;
}
2020-02-27 23:58:17 +00:00
static int janet_thread_start_child ( JanetMailboxPair * pair ) {
HANDLE handle = CreateThread ( NULL , 0 , janet_create_thread_wrapper , pair , 0 , NULL ) ;
2019-12-11 01:32:41 +00:00
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 ) {
2020-02-27 23:58:17 +00:00
thread_worker ( ( JanetMailboxPair * ) param ) ;
2019-11-27 05:13:53 +00:00
return NULL ;
}
2020-02-27 23:58:17 +00:00
static int janet_thread_start_child ( JanetMailboxPair * pair ) {
2019-12-02 02:47:22 +00:00
pthread_t handle ;
2020-02-27 23:58:17 +00:00
int error = pthread_create ( & handle , NULL , janet_pthread_wrapper , pair ) ;
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 ) {
2020-02-27 23:58:17 +00:00
janet_vm_mailbox = janet_mailbox_create ( 1 , 10 ) ;
2019-12-07 23:54:08 +00:00
}
2020-01-16 01:58:14 +00:00
janet_vm_thread_decode = NULL ;
janet_vm_thread_current = NULL ;
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 ;
2020-01-16 01:58:14 +00:00
janet_vm_thread_decode = NULL ;
2019-12-07 23:54:08 +00:00
}
2020-11-13 00:42:41 +00:00
JanetThread * janet_thread_current ( void ) {
if ( NULL = = janet_vm_thread_current ) {
janet_vm_thread_current = janet_make_thread ( janet_vm_mailbox , janet_get_core_table ( " make-image-dict " ) ) ;
janet_gcroot ( janet_wrap_abstract ( janet_vm_thread_current ) ) ;
}
return janet_vm_thread_current ;
}
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 ) ;
2020-11-13 00:42:41 +00:00
return janet_wrap_abstract ( janet_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 ) {
2020-06-13 14:42:16 +00:00
janet_arity ( argc , 1 , 3 ) ;
2019-12-18 20:07:46 +00:00
/* Just type checking */
janet_getfunction ( argv , 0 ) ;
int32_t cap = janet_optinteger ( argv , argc , 1 , 10 ) ;
2019-12-10 19:26:00 +00:00
if ( cap < 1 | | cap > UINT16_MAX ) {
janet_panicf ( " bad slot #1, expected integer in range [1, 65535], got %d " , cap ) ;
}
2020-06-14 19:20:38 +00:00
uint64_t flags = argc > = 3 ? janet_getflags ( argv , 2 , janet_thread_flags ) : JANET_THREAD_ABSTRACTS ;
2020-06-13 14:42:16 +00:00
JanetTable * encode ;
if ( flags & JANET_THREAD_HEAVYWEIGHT ) {
encode = janet_get_core_table ( " make-image-dict " ) ;
} else {
encode = NULL ;
}
2019-12-07 22:51:00 +00:00
2020-06-13 14:42:16 +00:00
JanetMailboxPair * pair = make_mailbox_pair ( janet_vm_mailbox , flags ) ;
2020-02-27 23:58:17 +00:00
JanetThread * thread = janet_make_thread ( pair - > newbox , encode ) ;
if ( janet_thread_start_child ( pair ) ) {
2020-02-28 15:04:28 +00:00
destroy_mailbox_pair ( pair ) ;
2019-12-05 03:04:43 +00:00
janet_panic ( " could not start thread " ) ;
2019-12-07 22:51:00 +00:00
}
2019-12-18 20:07:46 +00:00
2020-06-13 14:42:16 +00:00
if ( flags & JANET_THREAD_ABSTRACTS ) {
if ( janet_thread_send ( thread , janet_wrap_table ( janet_vm_abstract_registry ) , INFINITY ) ) {
janet_panic ( " could not send abstract registry to thread " ) ;
}
}
if ( flags & JANET_THREAD_CFUNCTIONS ) {
if ( janet_thread_send ( thread , janet_wrap_table ( janet_vm_registry ) , INFINITY ) ) {
janet_panic ( " could not send registry to thread " ) ;
}
}
2019-12-18 20:07:46 +00:00
/* If thread started, send the worker function. */
2019-12-18 21:07:06 +00:00
if ( janet_thread_send ( thread , argv [ 0 ] , INFINITY ) ) {
2019-12-18 20:49:57 +00:00
janet_panicf ( " could not send worker function %v to thread " , argv [ 0 ] ) ;
}
2019-12-18 20:07:46 +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 ) ;
2020-07-04 00:54:58 +00:00
case 2 :
janet_panicf ( " failed to receive message: %v " , out ) ;
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 ( ) ;
}
2020-07-03 21:19:05 +00:00
static Janet cfun_thread_exit ( int32_t argc , Janet * argv ) {
( void ) argv ;
janet_arity ( argc , 0 , 1 ) ;
# if defined(JANET_WINDOWS)
int32_t flag = janet_optinteger ( argv , argc , 0 , 0 ) ;
ExitThread ( flag ) ;
# else
pthread_exit ( NULL ) ;
# endif
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 }
} ;
2019-12-12 23:14:36 +00:00
static int janet_thread_getter ( void * p , Janet key , Janet * out ) {
2019-12-02 10:39:13 +00:00
( void ) p ;
2019-12-12 23:14:36 +00:00
if ( ! janet_checktype ( key , JANET_KEYWORD ) ) return 0 ;
return janet_getmethod ( janet_unwrap_keyword ( key ) , janet_thread_methods , out ) ;
2019-12-02 10:39:13 +00:00
}
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 ,
2020-06-13 14:42:16 +00:00
JDOC ( " (thread/new func &opt capacity flags) \n \n "
2019-12-18 20:07:46 +00:00
" Start a new thread that will start immediately. "
" If capacity is provided, that is how many messages can be stored in the thread's mailbox before blocking senders. "
2019-12-10 19:26:00 +00:00
" The capacity must be between 1 and 65535 inclusive, and defaults to 10. "
2021-01-04 17:28:23 +00:00
" Can optionally provide flags to the new thread - supported flags are: \n \n "
" * :h - Start a heavyweight thread. This loads the core environment by default, so may use more memory initially. Messages may compress better, though. \n \n "
" * :a - Allow sending over registered abstract types to the new thread \n \n "
" * :c - Send over cfunction information to the new thread. \n \n "
2019-12-10 19:26:00 +00:00
" Returns a handle to the new thread. " )
2019-12-02 02:28:12 +00:00
} ,
{
" thread/send " , cfun_thread_send ,
2020-09-13 13:38:37 +00:00
JDOC ( " (thread/send thread msgi &opt timeout) \n \n "
" Send a message to the thread. By default, the timeout is 1 second, but an optional timeout "
" in seconds can be provided. Use math/inf for no timeout. "
2019-12-02 02:28:12 +00:00
" 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 "
2020-09-13 13:38:37 +00:00
" Get a message sent to this thread. If timeout (in seconds) is provided, an error "
" will be thrown after the timeout has elapsed but "
" no messages are received. The default timeout is 1 second, and math/inf cam be passed to "
" turn off the timeout. " )
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. " )
} ,
2020-07-03 21:19:05 +00:00
{
" thread/exit " , cfun_thread_exit ,
JDOC ( " (thread/exit &opt code) \n \n "
" Exit from the current thread. If no more threads are running, ends the process, but otherwise does "
" not end the current process. " )
} ,
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 ) ;
2020-03-14 15:12:47 +00:00
janet_register_abstract_type ( & janet_thread_type ) ;
2019-11-27 05:13:53 +00:00
}
# endif