Skip to content

multithreaded uLisp #74

Description

@dragoncoder047

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions