about summary refs log tree commit diff
path: root/src/rt/rust_upcall.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rt/rust_upcall.cpp')
-rw-r--r--src/rt/rust_upcall.cpp654
1 files changed, 654 insertions, 0 deletions
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
new file mode 100644
index 00000000000..3a17ea1ce9a
--- /dev/null
+++ b/src/rt/rust_upcall.cpp
@@ -0,0 +1,654 @@
+
+#include "rust_internal.h"
+
+
+// Upcalls.
+
+#ifdef __GNUC__
+#define LOG_UPCALL_ENTRY(task)                              \
+    (task)->dom->get_log().reset_indent(0);                 \
+    (task)->dom->log(rust_log::UPCALL,                      \
+                     "upcall task: 0x%" PRIxPTR             \
+                     " retpc: 0x%" PRIxPTR,                 \
+                     (task), __builtin_return_address(0));  \
+    (task)->dom->get_log().indent();
+#else
+#define LOG_UPCALL_ENTRY(task)                              \
+    (task)->dom->get_log().reset_indent(0);                 \
+    (task)->dom->log(rust_log::UPCALL,                      \
+                     "upcall task: 0x%" PRIxPTR (task));    \
+    (task)->dom->get_log().indent();
+#endif
+
+extern "C" CDECL char const *str_buf(rust_task *task, rust_str *s);
+
+extern "C" void
+upcall_grow_task(rust_task *task, size_t n_frame_bytes)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->grow(n_frame_bytes);
+}
+
+extern "C" CDECL void
+upcall_log_int(rust_task *task, int32_t i)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::ULOG,
+                   "upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')",
+                   i, i, (char)i);
+}
+
+extern "C" CDECL void
+upcall_log_str(rust_task *task, rust_str *str)
+{
+    LOG_UPCALL_ENTRY(task);
+    const char *c = str_buf(task, str);
+    task->dom->log(rust_log::UPCALL|rust_log::ULOG,
+                   "upcall log_str(\"%s\")",
+                   c);
+}
+
+extern "C" CDECL void
+upcall_trace_word(rust_task *task, uintptr_t i)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::TRACE,
+                   "trace: 0x%" PRIxPTR "",
+                   i, i, (char)i);
+}
+
+extern "C" CDECL void
+upcall_trace_str(rust_task *task, char const *c)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::TRACE,
+                   "trace: %s",
+                   c);
+}
+
+extern "C" CDECL rust_port*
+upcall_new_port(rust_task *task, size_t unit_sz)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+             "upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)",
+             (uintptr_t)task, unit_sz);
+    return new (dom) rust_port(task, unit_sz);
+}
+
+extern "C" CDECL void
+upcall_del_port(rust_task *task, rust_port *port)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+                   "upcall del_port(0x%" PRIxPTR ")", (uintptr_t)port);
+    I(task->dom, !port->refcnt);
+    delete port;
+}
+
+extern "C" CDECL rust_chan*
+upcall_new_chan(rust_task *task, rust_port *port)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+             "upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
+             (uintptr_t)task, port);
+    I(dom, port);
+    return new (dom) rust_chan(task, port);
+}
+
+extern "C" CDECL void
+upcall_del_chan(rust_task *task, rust_chan *chan)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+             "upcall del_chan(0x%" PRIxPTR ")", (uintptr_t)chan);
+    I(dom, !chan->refcnt);
+    delete chan;
+}
+
+extern "C" CDECL rust_chan *
+upcall_clone_chan(rust_task *task, rust_task *owner, rust_chan *chan)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
+             "upcall clone_chan(owner 0x%" PRIxPTR ", chan 0x%" PRIxPTR ")",
+             (uintptr_t)owner, (uintptr_t)chan);
+    return new (owner->dom) rust_chan(owner, chan->port);
+}
+
+
+/*
+ * Buffering protocol:
+ *
+ *   - Reader attempts to read:
+ *     - Set reader to blocked-reading state.
+ *     - If buf with data exists:
+ *       - Attempt transmission.
+ *
+ *  - Writer attempts to write:
+ *     - Set writer to blocked-writing state.
+ *     - Copy data into chan.
+ *     - Attempt transmission.
+ *
+ *  - Transmission:
+ *       - Copy data from buf to reader
+ *       - Decr buf
+ *       - Set reader to running
+ *       - If buf now empty and blocked writer:
+ *         - Set blocked writer to running
+ *
+ */
+
+static int
+attempt_transmission(rust_dom *dom,
+                     rust_chan *src,
+                     rust_task *dst)
+{
+    I(dom, src);
+    I(dom, dst);
+
+    rust_port *port = src->port;
+    if (!port) {
+        dom->log(rust_log::COMM,
+                 "src died, transmission incomplete");
+        return 0;
+    }
+
+    circ_buf *buf = &src->buffer;
+    if (buf->unread == 0) {
+        dom->log(rust_log::COMM,
+                 "buffer empty, transmission incomplete");
+        return 0;
+    }
+
+    if (!dst->blocked_on(port)) {
+        dom->log(rust_log::COMM,
+                 "dst in non-reading state, transmission incomplete");
+        return 0;
+    }
+
+    uintptr_t *dptr = dst->dptr;
+    dom->log(rust_log::COMM,
+             "receiving %d bytes into dst_task=0x%" PRIxPTR
+             ", dptr=0x%" PRIxPTR,
+             port->unit_sz, dst, dptr);
+    buf->shift(dptr);
+
+    // Wake up the sender if its waiting for the send operation.
+    rust_task *sender = src->task;
+    rust_token *token = &src->token;
+    if (sender->blocked_on(token))
+        sender->wakeup(token);
+
+    // Wake up the receiver, there is new data.
+    dst->wakeup(port);
+
+    dom->log(rust_log::COMM, "transmission complete");
+    return 1;
+}
+
+extern "C" CDECL void
+upcall_yield(rust_task *task)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::COMM, "upcall yield()");
+    task->yield(1);
+}
+
+extern "C" CDECL void
+upcall_join(rust_task *task, rust_task *other)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::COMM,
+             "upcall join(other=0x%" PRIxPTR ")",
+             (uintptr_t)other);
+
+    // If the other task is already dying, we dont have to wait for it.
+    if (!other->dead()) {
+        other->waiting_tasks.push(&task->alarm);
+        task->block(other);
+        task->yield(2);
+    }
+}
+
+extern "C" CDECL void
+upcall_send(rust_task *task, rust_chan *chan, void *sptr)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::COMM,
+             "upcall send(chan=0x%" PRIxPTR ", sptr=0x%" PRIxPTR ")",
+             (uintptr_t)chan,
+             (uintptr_t)sptr);
+
+    I(dom, chan);
+    I(dom, sptr);
+
+    rust_port *port = chan->port;
+    dom->log(rust_log::MEM|rust_log::COMM,
+             "send to port", (uintptr_t)port);
+    I(dom, port);
+
+    rust_token *token = &chan->token;
+    dom->log(rust_log::MEM|rust_log::COMM,
+             "sending via token 0x%" PRIxPTR,
+             (uintptr_t)token);
+
+    if (port->task) {
+        chan->buffer.push(sptr);
+        task->block(token);
+        attempt_transmission(dom, chan, port->task);
+        if (chan->buffer.unread && !token->pending())
+            token->submit();
+    } else {
+        dom->log(rust_log::COMM|rust_log::ERR,
+                 "port has no task (possibly throw?)");
+    }
+
+    if (!task->running())
+        task->yield(3);
+}
+
+extern "C" CDECL void
+upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::COMM,
+             "upcall recv(dptr=0x" PRIxPTR ", port=0x%" PRIxPTR ")",
+             (uintptr_t)dptr,
+             (uintptr_t)port);
+
+    I(dom, port);
+    I(dom, port->task);
+    I(dom, task);
+    I(dom, port->task == task);
+
+    task->block(port);
+
+    if (port->writers.length() > 0) {
+        I(dom, task->dom);
+        size_t i = rand(&dom->rctx);
+        i %= port->writers.length();
+        rust_token *token = port->writers[i];
+        rust_chan *chan = token->chan;
+        if (attempt_transmission(dom, chan, task))
+            token->withdraw();
+    } else {
+        dom->log(rust_log::COMM,
+                 "no writers sending to port", (uintptr_t)port);
+    }
+
+    if (!task->running()) {
+        task->dptr = dptr;
+        task->yield(3);
+    }
+}
+
+extern "C" CDECL void
+upcall_fail(rust_task *task, char const *expr, char const *file, size_t line)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::ERR,
+                   "upcall fail '%s', %s:%" PRIdPTR,
+                   expr, file, line);
+    task->fail(4);
+}
+
+extern "C" CDECL void
+upcall_kill(rust_task *task, rust_task *target)
+{
+    LOG_UPCALL_ENTRY(task);
+    task->dom->log(rust_log::UPCALL|rust_log::TASK,
+                   "upcall kill target=0x%" PRIxPTR, target);
+    target->kill();
+}
+
+extern "C" CDECL void
+upcall_exit(rust_task *task)
+{
+    LOG_UPCALL_ENTRY(task);
+
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::TASK, "upcall exit");
+    task->die();
+    task->notify_waiting_tasks();
+    task->yield(1);
+}
+
+extern "C" CDECL uintptr_t
+upcall_malloc(rust_task *task, size_t nbytes)
+{
+    LOG_UPCALL_ENTRY(task);
+
+    void *p = task->dom->malloc(nbytes);
+    task->dom->log(rust_log::UPCALL|rust_log::MEM,
+                   "upcall malloc(%u) = 0x%" PRIxPTR,
+                   nbytes, (uintptr_t)p);
+    return (uintptr_t) p;
+}
+
+extern "C" CDECL void
+upcall_free(rust_task *task, void* ptr)
+{
+    LOG_UPCALL_ENTRY(task);
+
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall free(0x%" PRIxPTR ")",
+             (uintptr_t)ptr);
+    dom->free(ptr);
+}
+
+extern "C" CDECL rust_str *
+upcall_new_str(rust_task *task, char const *s, size_t fill)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall new_str('%s', %" PRIdPTR ")", s, fill);
+    size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
+    void *mem = dom->malloc(alloc);
+    if (!mem) {
+        task->fail(3);
+        return NULL;
+    }
+    rust_str *st = new (mem) rust_str(dom, alloc, fill, (uint8_t const *)s);
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
+             s, fill, st);
+    return st;
+}
+
+extern "C" CDECL rust_vec *
+upcall_new_vec(rust_task *task, size_t fill)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall new_vec(%" PRIdPTR ")", fill);
+    size_t alloc = next_power_of_two(sizeof(rust_vec) + fill);
+    void *mem = dom->malloc(alloc);
+    if (!mem) {
+        task->fail(3);
+        return NULL;
+    }
+    rust_vec *v = new (mem) rust_vec(dom, alloc, 0, NULL);
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall new_vec(%" PRIdPTR ") = 0x%" PRIxPTR,
+             fill, v);
+    return v;
+}
+
+
+extern "C" CDECL rust_str *
+upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM,
+             "upcall vec_grow(%" PRIxPTR ", %" PRIdPTR ")", v, n_bytes);
+    size_t alloc = next_power_of_two(sizeof(rust_vec) + v->fill + n_bytes);
+    if (v->refcnt == 1) {
+
+        // Fastest path: already large enough.
+        if (v->alloc >= alloc) {
+            dom->log(rust_log::UPCALL|rust_log::MEM, "no-growth path");
+            return v;
+        }
+
+        // Second-fastest path: can at least realloc.
+        dom->log(rust_log::UPCALL|rust_log::MEM, "realloc path");
+        v = (rust_vec*)dom->realloc(v, alloc);
+        if (!v) {
+            task->fail(3);
+            return NULL;
+        }
+        v->alloc = alloc;
+
+    } else {
+        // Slowest path: make a new vec.
+        dom->log(rust_log::UPCALL|rust_log::MEM, "new vec path");
+        void *mem = dom->malloc(alloc);
+        if (!mem) {
+            task->fail(3);
+            return NULL;
+        }
+        v->deref();
+        v = new (mem) rust_vec(dom, alloc, v->fill, &v->data[0]);
+    }
+    I(dom, sizeof(rust_vec) + v->fill <= v->alloc);
+    return v;
+}
+
+
+static rust_crate_cache::c_sym *
+fetch_c_sym(rust_task *task,
+            rust_crate const *curr_crate,
+            size_t lib_num,
+            size_t c_sym_num,
+            char const *library,
+            char const *symbol)
+{
+    rust_crate_cache *cache = task->get_crate_cache(curr_crate);
+    rust_crate_cache::lib *l = cache->get_lib(lib_num, library);
+    return cache->get_c_sym(c_sym_num, l, symbol);
+}
+
+extern "C" CDECL uintptr_t
+upcall_require_rust_sym(rust_task *task,
+                        rust_crate const *curr_crate,
+                        size_t lib_num,      // # of lib
+                        size_t c_sym_num,    // # of C sym "rust_crate" in lib
+                        size_t rust_sym_num, // # of rust sym
+                        char const *library,
+                        char const **path)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "upcall require rust sym: lib #%" PRIdPTR
+             " = %s, c_sym #%" PRIdPTR
+             ", rust_sym #%" PRIdPTR
+             ", curr_crate = 0x%" PRIxPTR,
+             lib_num, library, c_sym_num, rust_sym_num,
+             curr_crate);
+    for (char const **c = crate_rel(curr_crate, path); *c; ++c) {
+        dom->log(rust_log::UPCALL, " + %s", crate_rel(curr_crate, *c));
+    }
+
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "require C symbol 'rust_crate' from lib #%" PRIdPTR,lib_num);
+    rust_crate_cache::c_sym *c =
+        fetch_c_sym(task, curr_crate, lib_num, c_sym_num,
+                    library, "rust_crate");
+
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "require rust symbol inside crate");
+    rust_crate_cache::rust_sym *s =
+        task->cache->get_rust_sym(rust_sym_num, dom, curr_crate, c, path);
+
+    uintptr_t addr = s->get_val();
+    if (addr) {
+        dom->log(rust_log::UPCALL|rust_log::CACHE,
+                 "found-or-cached addr: 0x%" PRIxPTR, addr);
+    } else {
+        dom->log(rust_log::UPCALL|rust_log::CACHE,
+                 "failed to resolve symbol");
+        task->fail(7);
+    }
+    return addr;
+}
+
+extern "C" CDECL uintptr_t
+upcall_require_c_sym(rust_task *task,
+                     rust_crate const *curr_crate,
+                     size_t lib_num,      // # of lib
+                     size_t c_sym_num,    // # of C sym
+                     char const *library,
+                     char const *symbol)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "upcall require c sym: lib #%" PRIdPTR
+             " = %s, c_sym #%" PRIdPTR
+             " = %s"
+             ", curr_crate = 0x%" PRIxPTR,
+             lib_num, library, c_sym_num, symbol, curr_crate);
+
+    rust_crate_cache::c_sym *c =
+        fetch_c_sym(task, curr_crate, lib_num, c_sym_num, library, symbol);
+
+    uintptr_t addr = c->get_val();
+    if (addr) {
+        dom->log(rust_log::UPCALL|rust_log::CACHE,
+                 "found-or-cached addr: 0x%" PRIxPTR, addr);
+    } else {
+        dom->log(rust_log::UPCALL|rust_log::CACHE,
+                 "failed to resolve symbol");
+        task->fail(6);
+    }
+    return addr;
+}
+
+extern "C" CDECL type_desc *
+upcall_get_type_desc(rust_task *task,
+                     rust_crate const *curr_crate,
+                     size_t size,
+                     size_t align,
+                     size_t n_descs,
+                     type_desc const **descs)
+{
+    LOG_UPCALL_ENTRY(task);
+    rust_dom *dom = task->dom;
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "upcall get_type_desc with size=%" PRIdPTR
+             ", align=%" PRIdPTR ", %" PRIdPTR " descs",
+             size, align, n_descs);
+    rust_crate_cache *cache = task->get_crate_cache(curr_crate);
+    type_desc *td = cache->get_type_desc(size, align, n_descs, descs);
+    dom->log(rust_log::UPCALL|rust_log::CACHE,
+             "returning tydesc 0x%" PRIxPTR, td);
+    return td;
+}
+
+
+#if defined(__WIN32__)
+static DWORD WINAPI rust_thread_start(void *ptr)
+#elif defined(__GNUC__)
+static void *rust_thread_start(void *ptr)
+#else
+#error "Platform not supported"
+#endif
+{
+    // We were handed the domain we are supposed to run.
+    rust_dom *dom = (rust_dom *)ptr;
+
+    // Start a new rust main loop for this thread.
+    rust_main_loop(dom);
+
+    rust_srv *srv = dom->srv;
+    delete dom;
+    delete srv;
+
+    return 0;
+}
+
+extern "C" CDECL rust_task *
+upcall_new_task(rust_task *spawner)
+{
+    LOG_UPCALL_ENTRY(spawner);
+
+    rust_dom *dom = spawner->dom;
+    rust_task *task = new (dom) rust_task(dom, spawner);
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+             "upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR,
+             spawner, task);
+    return task;
+}
+
+extern "C" CDECL rust_task *
+upcall_start_task(rust_task *spawner,
+                  rust_task *task,
+                  uintptr_t exit_task_glue,
+                  uintptr_t spawnee_fn,
+                  size_t callsz)
+{
+    LOG_UPCALL_ENTRY(spawner);
+
+    rust_dom *dom = spawner->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+             "upcall start_task(task 0x%" PRIxPTR
+             " exit_task_glue 0x%" PRIxPTR
+             ", spawnee 0x%" PRIxPTR
+             ", callsz %" PRIdPTR ")",
+             task, exit_task_glue, spawnee_fn, callsz);
+    task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
+    return task;
+}
+
+extern "C" CDECL rust_task *
+upcall_new_thread(rust_task *task)
+{
+    LOG_UPCALL_ENTRY(task);
+
+    rust_dom *old_dom = task->dom;
+    rust_dom *new_dom = new rust_dom(old_dom->srv->clone(),
+                                     old_dom->root_crate);
+    new_dom->log(rust_log::UPCALL|rust_log::MEM,
+                 "upcall new_thread() = 0x%" PRIxPTR,
+                 new_dom->root_task);
+    return new_dom->root_task;
+}
+
+extern "C" CDECL rust_task *
+upcall_start_thread(rust_task *spawner,
+                    rust_task *root_task,
+                    uintptr_t exit_task_glue,
+                    uintptr_t spawnee_fn,
+                    size_t callsz)
+{
+    LOG_UPCALL_ENTRY(spawner);
+
+    rust_dom *dom = spawner->dom;
+    dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
+             "upcall start_thread(exit_task_glue 0x%" PRIxPTR
+             ", spawnee 0x%" PRIxPTR
+             ", callsz %" PRIdPTR ")",
+             exit_task_glue, spawnee_fn, callsz);
+    root_task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
+
+#if defined(__WIN32__)
+    HANDLE thread;
+    thread = CreateThread(NULL, 0, rust_thread_start, root_task->dom,
+                          0, NULL);
+    dom->win32_require("CreateThread", thread != NULL);
+#else
+    pthread_t thread;
+    pthread_create(&thread, &dom->attr, rust_thread_start,
+                   (void *)root_task->dom);
+#endif
+
+    return 0;
+}
+
+//
+// Local Variables:
+// mode: C++
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+// End:
+//