I had another idea that would require editing the uLisp source (i.e. not an extension): making the multithreading feature of the esp32's FreeRTOS available to uLisp. I wish it was also available on Teensy too but there aren't any good libraries for that. You could (ab)use setjmp()/longjmp() to switch contexts but I'm not sure how well that would work.
To be able to preserve the atomic nature of garbage collections, the garbage collector can't call yield() and must have some way to mark all the running threads.
The changes would look something like this
typedef struct {
jmp_buf top_handler;
jmp_buf* current_handler;
uint16_t flags;
object* code;
object* env;
object* send_queue;
object** send_tail;
object* recv_queue;
object** recv_tail;
TaskHandle_t handle;
bool running;
} task_locals;
#define setflag(locals, flag) ((locals)->flags |= (1 << (flag)))
#define clrflag(locals, flag) ((locals)->flags &= ~(1 << (flag)))
#define tstflag(locals, flag) ((locals)->flags & (1 << (flag)))
// error() and error2() and related functions take first parameter task_locals* to get right jmp_buf
#define MAX_THREADS 64
task_locals uLisp_Tasks[MAX_THREADS];
typedef object* (*fn_ptr_type)(task_locals*, object*, object*);
void thread_fun (void* arg) {
task_locals* task = (task_locals*)arg;
eval(task->code, task->env);
task->running = false;
vTaskDelete(task->handle);
}
object* fn_spawn (task_locals* my_task, object* args, object* env) {
int tid;
for (tid = 0; tid < MAX_TASKS; tid++) {
if (uLisp_Tasks[tid].running == false) break;
}
if (tid = MAX_TASKS) error2(my_task, PSTR("too many tasks"));
task_locals* new_task = &uLisp_Tasks[tid];
new_task->flags = my_task->flags;
new_task->code = first(args);
new_task->env = NULL;
new_task->current_handler = &new_task->top_handler;
new_task->env = NULL; // or env to inherit variables
new_task->send_queue = NULL;
new_task->send_tail = NULL;
new_task->recv_queue = NULL;
new_task->recv_tail = NULL;
new_task->running = true;
xTaskCreate(thread_fun, "uLisp-spawn", 10000, (void*)new_task, 1, &new_task->handle);
return number(tid);
}
And then any of the functions that refer to global variables, could instead look in the
Then you could implement functions like (spawn), (send), (receive), and (kill) for threads to be started and stopped and for them to communicate between each other.
I had another idea that would require editing the uLisp source (i.e. not an extension): making the multithreading feature of the esp32's FreeRTOS available to uLisp. I wish it was also available on Teensy too but there aren't any good libraries for that. You could (ab)use
setjmp()/longjmp()to switch contexts but I'm not sure how well that would work.To be able to preserve the atomic nature of garbage collections, the garbage collector can't call
yield()and must have some way to mark all the running threads.The changes would look something like this
And then any of the functions that refer to global variables, could instead look in the
Then you could implement functions like
(spawn),(send),(receive), and(kill)for threads to be started and stopped and for them to communicate between each other.