#include "runtime.h"
#include "mem/heap.h"
#include "mem/sys.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef RAY_OS_WINDOWS
#include <windows.h>
#else
#include <unistd.h>
#endif
extern ray_err_t ray_lang_init(void);
extern void ray_lang_destroy(void);
ray_runtime_t *__RUNTIME = NULL;
_Thread_local ray_vm_t *__VM = NULL;
ray_t __ray_null = { .type = RAY_NULL, .attrs = RAY_ATTR_ARENA, .rc = 0, .len = 0 };
ray_t __ray_oom = {
.type = RAY_ERROR,
.attrs = RAY_ATTR_ARENA,
.rc = 0,
.slen = 3,
.sdata = { 'o', 'o', 'm', 0, 0, 0, 0 },
};
const char* ray_err_code_str(ray_err_t e) {
static const char* codes[] = {
[RAY_OK] = "ok",
[RAY_ERR_OOM] = "oom",
[RAY_ERR_TYPE] = "type",
[RAY_ERR_RANGE] = "range",
[RAY_ERR_LENGTH] = "length",
[RAY_ERR_RANK] = "rank",
[RAY_ERR_DOMAIN] = "domain",
[RAY_ERR_NYI] = "nyi",
[RAY_ERR_IO] = "io",
[RAY_ERR_SCHEMA] = "schema",
[RAY_ERR_CORRUPT] = "corrupt",
[RAY_ERR_CANCEL] = "cancel",
[RAY_ERR_PARSE] = "parse",
[RAY_ERR_NAME] = "name",
[RAY_ERR_LIMIT] = "limit",
[RAY_ERR_RESERVED] = "reserve",
};
if ((unsigned)e >= sizeof(codes)/sizeof(codes[0])) return "error";
return codes[e];
}
ray_err_t ray_err_from_obj(ray_t* err) {
if (!err || err->type != RAY_ERROR) return RAY_ERR_DOMAIN;
const char* s = err->sdata;
int n = err->slen;
static const struct { const char* s; int len; ray_err_t e; } map[] = {
{"oom", 3, RAY_ERR_OOM}, {"type", 4, RAY_ERR_TYPE},
{"range", 5, RAY_ERR_RANGE}, {"length", 6, RAY_ERR_LENGTH},
{"rank", 4, RAY_ERR_RANK}, {"domain", 6, RAY_ERR_DOMAIN},
{"nyi", 3, RAY_ERR_NYI}, {"io", 2, RAY_ERR_IO},
{"schema", 6, RAY_ERR_SCHEMA}, {"corrupt", 7, RAY_ERR_CORRUPT},
{"cancel", 6, RAY_ERR_CANCEL}, {"parse", 5, RAY_ERR_PARSE},
{"name", 4, RAY_ERR_NAME}, {"limit", 5, RAY_ERR_LIMIT},
{"reserve", 7, RAY_ERR_RESERVED},
};
for (int i = 0; i < (int)(sizeof(map)/sizeof(map[0])); i++)
if (n == map[i].len && memcmp(s, map[i].s, n) == 0) return map[i].e;
return RAY_ERR_DOMAIN;
}
static ray_t* ray_verror(const char* code, const char* fmt, va_list ap) {
if (__VM) {
if (fmt) vsnprintf(__VM->err.msg, sizeof(__VM->err.msg), fmt, ap);
else __VM->err.msg[0] = '\0';
}
ray_t* err = ray_alloc(0);
if (!err) return &__ray_oom;
err->type = RAY_ERROR;
err->slen = 0;
memset(err->sdata, 0, 7);
if (code) {
size_t len = strlen(code);
if (len > 7) len = 7;
memcpy(err->sdata, code, len);
err->slen = (uint8_t)len;
}
return err;
}
ray_t* ray_error(const char* code, const char* fmt, ...) {
if (fmt) {
va_list ap;
va_start(ap, fmt);
ray_t* err = ray_verror(code, fmt, ap);
va_end(ap);
return err;
}
if (__VM) __VM->err.msg[0] = '\0';
ray_t* err = ray_alloc(0);
if (!err) return &__ray_oom;
err->type = RAY_ERROR;
err->slen = 0;
memset(err->sdata, 0, 7);
if (code) {
size_t len = strlen(code);
if (len > 7) len = 7;
memcpy(err->sdata, code, len);
err->slen = (uint8_t)len;
}
return err;
}
void ray_error_free(ray_t* err) {
if (!err || !RAY_IS_ERR(err)) return;
if (err == RAY_OOM_OBJ) return;
err->type = -RAY_I64;
ray_free(err);
}
const char* ray_err_code(ray_t* err) {
if (!err || err->type != RAY_ERROR) return NULL;
static _Thread_local char buf[8];
memcpy(buf, err->sdata, err->slen);
buf[err->slen] = '\0';
return buf;
}
const char* ray_error_msg(void) {
if (!__VM || !__VM->err.msg[0]) return NULL;
return __VM->err.msg;
}
void ray_error_clear(void) {
if (__VM) __VM->err.msg[0] = '\0';
}
static ray_runtime_t* runtime_create_impl(const char* sym_path,
ray_err_t* out_sym_err) {
if (out_sym_err) *out_sym_err = RAY_OK;
ray_heap_init();
ray_sym_init();
ray_runtime_t* rt = (ray_runtime_t*)ray_sys_alloc(sizeof(ray_runtime_t));
if (!rt) return NULL;
memset(rt, 0, sizeof(*rt));
rt->n_vms = 1;
rt->vms = (ray_vm_t**)ray_sys_alloc(sizeof(ray_vm_t*));
if (!rt->vms) { ray_sys_free(rt); return NULL; }
rt->vms[0] = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t));
if (!rt->vms[0]) { ray_sys_free(rt->vms); ray_sys_free(rt); return NULL; }
memset(rt->vms[0], 0, sizeof(ray_vm_t));
rt->vms[0]->id = 0;
__VM = rt->vms[0];
#ifdef RAY_OS_WINDOWS
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
if (GlobalMemoryStatusEx(&ms))
rt->mem_budget = (int64_t)(ms.ullTotalPhys * 0.8);
else
rt->mem_budget = (int64_t)(4ULL << 30);
#else
long pages = sysconf(_SC_PHYS_PAGES);
long psize = sysconf(_SC_PAGESIZE);
if (pages > 0 && psize > 0)
rt->mem_budget = (int64_t)((double)pages * (double)psize * 0.8);
else
rt->mem_budget = (int64_t)(4ULL << 30);
#endif
__RUNTIME = rt;
if (sym_path) {
struct stat st;
if (stat(sym_path, &st) == 0) {
if (st.st_size > 0 &&
(int64_t)st.st_size > rt->mem_budget / 2) {
if (out_sym_err) *out_sym_err = RAY_ERR_OOM;
} else {
ray_err_t sym_err = ray_sym_load(sym_path);
if (out_sym_err) *out_sym_err = sym_err;
}
} else if (errno != ENOENT) {
if (out_sym_err) *out_sym_err = RAY_ERR_IO;
}
}
ray_lang_init();
return rt;
}
ray_runtime_t* ray_runtime_create(int argc, char** argv) {
(void)argc; (void)argv;
return runtime_create_impl(NULL, NULL);
}
ray_runtime_t* ray_runtime_create_with_sym(const char* sym_path) {
return runtime_create_impl(sym_path, NULL);
}
ray_runtime_t* ray_runtime_create_with_sym_err(const char* sym_path,
ray_err_t* out_sym_err) {
return runtime_create_impl(sym_path, out_sym_err);
}
void ray_runtime_set_poll(void* poll) {
if (__RUNTIME) __RUNTIME->poll = poll;
}
void* ray_runtime_get_poll(void) {
return __RUNTIME ? __RUNTIME->poll : NULL;
}
int64_t ray_mem_budget(void) {
return __RUNTIME ? __RUNTIME->mem_budget : 0;
}
bool ray_mem_pressure(void) {
if (!__RUNTIME) return false;
ray_mem_stats_t st;
ray_mem_stats(&st);
return (int64_t)(st.bytes_allocated + st.direct_bytes) > __RUNTIME->mem_budget;
}
void ray_runtime_destroy(ray_runtime_t* rt) {
if (!rt) return;
ray_lang_destroy();
for (int32_t i = 0; i < rt->n_vms; i++) {
ray_vm_t* vm = rt->vms[i];
if (vm->raise_val) ray_release(vm->raise_val);
if (vm->trace) { ray_release(vm->trace); vm->trace = NULL; }
ray_sys_free(vm);
}
ray_sys_free(rt->vms);
__VM = NULL;
__RUNTIME = NULL;
ray_sym_destroy();
ray_heap_destroy();
ray_sys_free(rt);
}