nshell/src/job.c

133 lines
3.7 KiB
C

#include "job.h"
#include <string.h>
#include <gint/kmalloc.h>
#include "term.h"
#include "uns_wren_utils.h"
extern jmp_buf sched_ctxbuf;
WrenVM *job_vms[UNS_MAX_JOBS] = {0};
void *job_stacks[UNS_MAX_JOBS] = {0};
int current_job_id = -1;
static const char *new_job_name;
static const char *new_job_source;
extern volatile int must_yield;
__attribute__((noreturn)) static void run_vm(void) {
const int job_id = current_job_id;
// term_kprint("copy input data...");
const int name_len = strlen(new_job_name);
const int prgm_len = strlen(new_job_source);
const int total_len = name_len + prgm_len + 2;
char *prgm_buf = kmalloc(total_len, NULL);
if (prgm_buf == NULL) {
term_eprintf("kmalloc(%d, NULL) failed", total_len);
longjmp(sched_ctxbuf, 2); // job exit point
}
strcpy(prgm_buf, new_job_name);
strcpy(prgm_buf + name_len + 1, new_job_source);
// now the gloabl static strings can be freed
// term_kprint("create wren VM...");
WrenConfiguration config;
init_wren_config(&config);
must_yield = 0;
WrenVM *vm = wrenNewVM(&config);
job_vms[job_id] = vm; // place the newly created vm in its slot for later resume
// term_kprint("start interpreter...");
must_yield = 1; // yield ASAP
wrenInterpret(vm, new_job_name, new_job_source);
// term_kprint("interpreter returned");
// free resources (stack will be freed by the scheduler)
wrenFreeVM(job_vms[job_id]);
job_vms[job_id] = NULL;
kfree(prgm_buf);
// never return !!!
// longjmp to scheduler instead
// return value of 2 means job exit
longjmp(sched_ctxbuf, 2);
}
static void *job_start_(const uint32_t entry_point) {
term_kprint("allocate new stack...");
static const int job_stack_size = 48 * 1024; // 48 kiB stack
const uint32_t child_stack = (uint32_t)kmalloc(job_stack_size, NULL);
const uint32_t new_sp = ((child_stack + 3) >> 2 << 2) + ((job_stack_size + 3) >> 2 << 2);
term_kprintf("new_bp=0x%08x", child_stack);
term_kprintf("new_sp=0x%08x", new_sp);
// get a copy of the current context
jmp_buf ctx;
setjmp(ctx);
// transform it to the hidden representation so we can modify it
struct __jmp_buf *ctx_ptr = &ctx[0];
ctx_ptr->reg[7] = new_sp; // change stack pointer
ctx_ptr->reg[6] = new_sp; // change frame pointer
ctx_ptr->pr = entry_point; // change pc backup
const int ret = setjmp(sched_ctxbuf);
if (ret == 0)
longjmp(ctx, 1); // apply changes, run job until it yields a first time
else
return (void *)child_stack; // then return the stack descriptor (not "stack pointer"!) for later free
}
int job_start(const char *name, const char *source) {
new_job_name = name;
new_job_source = source;
for (int i = 0; i < UNS_MAX_JOBS; i++) {
if (job_vms[i] == NULL) {
current_job_id = i;
job_stacks[i] = job_start_((uint32_t)run_vm);
return i;
}
}
// no slot found
return -1;
}
void job_resume(int job_id) {
wrenResume(job_vms[job_id]); // longjmp(vm->ctxbuf, 1);
}
void job_GC(int job_id) { wrenCollectGarbage(job_vms[job_id]); }
void job_free(int job_id) {
// just in case something failed
if (job_vms[job_id] != NULL) {
wrenFreeVM(job_vms[job_id]);
job_vms[job_id] = NULL;
}
kfree(job_stacks[job_id]);
job_stacks[job_id] = NULL;
}
void next_job(void) {
if (current_job_id < 0)
current_job_id = 0;
for (int i = 0; i < UNS_MAX_JOBS; i++) {
const int candidate = (current_job_id + i + 1) % UNS_MAX_JOBS;
if (job_stacks[candidate] != NULL) {
current_job_id = candidate;
return;
}
}
current_job_id = -1;
}