mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-23 20:34:06 +00:00
Added a message passing system based on lock free queues for inter-thread communication. Channels now buffer on the sending side, and no longer require blocking when sending. Lots of other refactoring and bug fixes.
This commit is contained in:
parent
1f0656d908
commit
00d1465d13
23
src/Makefile
23
src/Makefile
@ -244,7 +244,10 @@ BOOT_CMXS := $(BOOT_MLS:.ml=.cmx)
|
|||||||
BOOT_OBJS := $(BOOT_MLS:.ml=.o)
|
BOOT_OBJS := $(BOOT_MLS:.ml=.o)
|
||||||
BOOT_CMIS := $(BOOT_MLS:.ml=.cmi)
|
BOOT_CMIS := $(BOOT_MLS:.ml=.cmi)
|
||||||
|
|
||||||
RUNTIME_CS := rt/rust.cpp \
|
RUNTIME_CS := rt/sync/spin_lock.cpp \
|
||||||
|
rt/sync/lock_free_queue.cpp \
|
||||||
|
rt/sync/condition_variable.cpp \
|
||||||
|
rt/rust.cpp \
|
||||||
rt/rust_builtin.cpp \
|
rt/rust_builtin.cpp \
|
||||||
rt/rust_crate.cpp \
|
rt/rust_crate.cpp \
|
||||||
rt/rust_crate_cache.cpp \
|
rt/rust_crate_cache.cpp \
|
||||||
@ -256,12 +259,19 @@ RUNTIME_CS := rt/rust.cpp \
|
|||||||
rt/rust_upcall.cpp \
|
rt/rust_upcall.cpp \
|
||||||
rt/rust_log.cpp \
|
rt/rust_log.cpp \
|
||||||
rt/rust_timer.cpp \
|
rt/rust_timer.cpp \
|
||||||
|
rt/circular_buffer.cpp \
|
||||||
rt/isaac/randport.cpp
|
rt/isaac/randport.cpp
|
||||||
|
|
||||||
RUNTIME_HDR := rt/rust.h \
|
RUNTIME_HDR := rt/globals.h \
|
||||||
|
rt/rust.h \
|
||||||
rt/rust_dwarf.h \
|
rt/rust_dwarf.h \
|
||||||
rt/rust_internal.h \
|
rt/rust_internal.h \
|
||||||
rt/rust_util.h
|
rt/rust_util.h \
|
||||||
|
rt/rust_chan.h \
|
||||||
|
rt/rust_dom.h \
|
||||||
|
rt/rust_task.h \
|
||||||
|
rt/rust_proxy.h \
|
||||||
|
rt/circular_buffer.h
|
||||||
|
|
||||||
RUNTIME_INCS := -Irt/isaac -Irt/uthash
|
RUNTIME_INCS := -Irt/isaac -Irt/uthash
|
||||||
RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX))
|
RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX))
|
||||||
@ -363,6 +373,8 @@ TEST_XFAILS_X86 := $(MUT_BOX_XFAILS) \
|
|||||||
test/run-pass/task-comm.rs \
|
test/run-pass/task-comm.rs \
|
||||||
test/run-pass/vec-alloc-append.rs \
|
test/run-pass/vec-alloc-append.rs \
|
||||||
test/run-pass/vec-slice.rs \
|
test/run-pass/vec-slice.rs \
|
||||||
|
test/run-pass/task-comm-3.rs \
|
||||||
|
test/run-pass/task-comm-4.rs \
|
||||||
test/compile-fail/bad-recv.rs \
|
test/compile-fail/bad-recv.rs \
|
||||||
test/compile-fail/bad-send.rs \
|
test/compile-fail/bad-send.rs \
|
||||||
test/compile-fail/infinite-tag-type-recursion.rs \
|
test/compile-fail/infinite-tag-type-recursion.rs \
|
||||||
@ -452,6 +464,11 @@ TEST_XFAILS_LLVM := $(addprefix test/run-pass/, \
|
|||||||
tail-cps.rs \
|
tail-cps.rs \
|
||||||
tail-direct.rs \
|
tail-direct.rs \
|
||||||
task-comm.rs \
|
task-comm.rs \
|
||||||
|
task-comm-0.rs \
|
||||||
|
task-comm-1.rs \
|
||||||
|
task-comm-2.rs \
|
||||||
|
task-comm-3.rs \
|
||||||
|
task-comm-4.rs \
|
||||||
threads.rs \
|
threads.rs \
|
||||||
tup.rs \
|
tup.rs \
|
||||||
type-sizes.rs \
|
type-sizes.rs \
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
let rc_base_field_refcnt = 0;;
|
let rc_base_field_refcnt = 0;;
|
||||||
|
|
||||||
let task_field_refcnt = rc_base_field_refcnt;;
|
let task_field_refcnt = rc_base_field_refcnt;;
|
||||||
let task_field_stk = task_field_refcnt + 1;;
|
let task_field_stk = task_field_refcnt + 2;;
|
||||||
let task_field_runtime_sp = task_field_stk + 1;;
|
let task_field_runtime_sp = task_field_stk + 1;;
|
||||||
let task_field_rust_sp = task_field_runtime_sp + 1;;
|
let task_field_rust_sp = task_field_runtime_sp + 1;;
|
||||||
let task_field_gc_alloc_chain = task_field_rust_sp + 1;;
|
let task_field_gc_alloc_chain = task_field_rust_sp + 1;;
|
||||||
|
118
src/rt/circular_buffer.cpp
Normal file
118
src/rt/circular_buffer.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* A simple resizable circular buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rust_internal.h"
|
||||||
|
|
||||||
|
circular_buffer::circular_buffer(rust_dom *dom, size_t unit_sz) :
|
||||||
|
dom(dom),
|
||||||
|
_buffer_sz(INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz),
|
||||||
|
unit_sz(unit_sz),
|
||||||
|
_next(0),
|
||||||
|
_unread(0),
|
||||||
|
_buffer((uint8_t *)dom->calloc(_buffer_sz)) {
|
||||||
|
|
||||||
|
A(dom, unit_sz, "Unit size must be larger than zero.");
|
||||||
|
|
||||||
|
dom->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"new circular_buffer(buffer_sz=%d, unread=%d)"
|
||||||
|
"-> circular_buffer=0x%" PRIxPTR,
|
||||||
|
_buffer_sz, _unread, this);
|
||||||
|
|
||||||
|
A(dom, _buffer, "Failed to allocate buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
circular_buffer::~circular_buffer() {
|
||||||
|
dom->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"~circular_buffer 0x%" PRIxPTR,
|
||||||
|
this);
|
||||||
|
I(dom, _buffer);
|
||||||
|
// I(dom, unread == 0);
|
||||||
|
dom->free(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the unread data from this buffer to the "dst" address.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
circular_buffer::transfer(void *dst) {
|
||||||
|
I(dom, dst);
|
||||||
|
uint8_t *ptr = (uint8_t *) dst;
|
||||||
|
for (size_t i = 0; i < _unread; i += unit_sz) {
|
||||||
|
memcpy(&ptr[i], &_buffer[_next + i % _buffer_sz], unit_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the data at the "src" address into this buffer. The buffer is
|
||||||
|
* grown if it isn't large enough.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
circular_buffer::enqueue(void *src) {
|
||||||
|
I(dom, src);
|
||||||
|
I(dom, _unread <= _buffer_sz);
|
||||||
|
|
||||||
|
// Grow if necessary.
|
||||||
|
if (_unread == _buffer_sz) {
|
||||||
|
I(dom, _buffer_sz <= MAX_CIRCULAR_BUFFFER_SIZE);
|
||||||
|
void *tmp = dom->malloc(_buffer_sz << 1);
|
||||||
|
transfer(tmp);
|
||||||
|
_buffer_sz <<= 1;
|
||||||
|
dom->free(_buffer);
|
||||||
|
_buffer = (uint8_t *)tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
dom->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"circular_buffer enqueue "
|
||||||
|
"unread: %d, buffer_sz: %d, unit_sz: %d",
|
||||||
|
_unread, _buffer_sz, unit_sz);
|
||||||
|
|
||||||
|
I(dom, _unread < _buffer_sz);
|
||||||
|
I(dom, _unread + unit_sz <= _buffer_sz);
|
||||||
|
|
||||||
|
// Copy data
|
||||||
|
size_t i = (_next + _unread) % _buffer_sz;
|
||||||
|
memcpy(&_buffer[i], src, unit_sz);
|
||||||
|
_unread += unit_sz;
|
||||||
|
|
||||||
|
dom->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"circular_buffer pushed data at index: %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data from this buffer to the "dst" address. The buffer is
|
||||||
|
* shrunk if possible.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
circular_buffer::dequeue(void *dst) {
|
||||||
|
I(dom, dst);
|
||||||
|
I(dom, unit_sz > 0);
|
||||||
|
I(dom, _unread >= unit_sz);
|
||||||
|
I(dom, _unread <= _buffer_sz);
|
||||||
|
I(dom, _buffer);
|
||||||
|
size_t i = _next;
|
||||||
|
memcpy(dst, &_buffer[i], unit_sz);
|
||||||
|
dom->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"shifted data from index %d", i);
|
||||||
|
_unread -= unit_sz;
|
||||||
|
_next += unit_sz;
|
||||||
|
I(dom, _next <= _buffer_sz);
|
||||||
|
if (_next == _buffer_sz) {
|
||||||
|
_next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink if possible.
|
||||||
|
if (_buffer_sz >= INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz &&
|
||||||
|
_unread <= _buffer_sz / 4) {
|
||||||
|
void *tmp = dom->malloc(_buffer_sz / 2);
|
||||||
|
transfer(tmp);
|
||||||
|
_buffer_sz >>= 1;
|
||||||
|
dom->free(_buffer);
|
||||||
|
_buffer = (uint8_t *)tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
circular_buffer::is_empty() {
|
||||||
|
return _unread == 0;
|
||||||
|
}
|
30
src/rt/circular_buffer.h
Normal file
30
src/rt/circular_buffer.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CIRCULAR_BUFFER_H
|
||||||
|
#define CIRCULAR_BUFFER_H
|
||||||
|
|
||||||
|
class
|
||||||
|
circular_buffer : public dom_owned<circular_buffer> {
|
||||||
|
static const size_t INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS = 8;
|
||||||
|
static const size_t MAX_CIRCULAR_BUFFFER_SIZE = 1 << 24;
|
||||||
|
|
||||||
|
public:
|
||||||
|
rust_dom *dom;
|
||||||
|
circular_buffer(rust_dom *dom, size_t unit_sz);
|
||||||
|
~circular_buffer();
|
||||||
|
void transfer(void *dst);
|
||||||
|
void enqueue(void *src);
|
||||||
|
void dequeue(void *dst);
|
||||||
|
bool is_empty();
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t _buffer_sz;
|
||||||
|
size_t unit_sz;
|
||||||
|
size_t _next;
|
||||||
|
size_t _unread;
|
||||||
|
uint8_t *_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* CIRCULAR_BUFFER_H */
|
33
src/rt/globals.h
Normal file
33
src/rt/globals.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef GLOBALS_H
|
||||||
|
#define GLOBALS_H
|
||||||
|
|
||||||
|
#define __STDC_LIMIT_MACROS 1
|
||||||
|
#define __STDC_CONSTANT_MACROS 1
|
||||||
|
#define __STDC_FORMAT_MACROS 1
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
extern "C" {
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
}
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#else
|
||||||
|
#error "Platform not supported."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* GLOBALS_H */
|
@ -1,7 +1,6 @@
|
|||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
#include "util/array_list.h"
|
#include "util/array_list.h"
|
||||||
|
|
||||||
|
|
||||||
// #define TRACK_ALLOCATIONS
|
// #define TRACK_ALLOCATIONS
|
||||||
// For debugging, keeps track of live allocations, so you can find out
|
// For debugging, keeps track of live allocations, so you can find out
|
||||||
// exactly what leaked.
|
// exactly what leaked.
|
||||||
@ -100,52 +99,6 @@ rust_srv::clone()
|
|||||||
return new rust_srv();
|
return new rust_srv();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
rust_main_loop(rust_dom *dom)
|
|
||||||
{
|
|
||||||
// Make sure someone is watching, to pull us out of infinite loops.
|
|
||||||
rust_timer timer(*dom);
|
|
||||||
|
|
||||||
int rval;
|
|
||||||
rust_task *task;
|
|
||||||
|
|
||||||
dom->log(rust_log::DOM,
|
|
||||||
"running main-loop on domain 0x%" PRIxPTR, dom);
|
|
||||||
dom->logptr("exit-task glue",
|
|
||||||
dom->root_crate->get_exit_task_glue());
|
|
||||||
|
|
||||||
while ((task = dom->sched()) != NULL) {
|
|
||||||
I(dom, task->running());
|
|
||||||
|
|
||||||
dom->log(rust_log::TASK,
|
|
||||||
"activating task 0x%" PRIxPTR ", sp=0x%" PRIxPTR,
|
|
||||||
(uintptr_t)task, task->rust_sp);
|
|
||||||
|
|
||||||
dom->interrupt_flag = 0;
|
|
||||||
|
|
||||||
dom->activate(task);
|
|
||||||
|
|
||||||
dom->log(rust_log::TASK,
|
|
||||||
"returned from task 0x%" PRIxPTR
|
|
||||||
" in state '%s', sp=0x%" PRIxPTR,
|
|
||||||
(uintptr_t)task,
|
|
||||||
dom->state_vec_name(task->state),
|
|
||||||
task->rust_sp);
|
|
||||||
|
|
||||||
I(dom, task->rust_sp >= (uintptr_t) &task->stk->data[0]);
|
|
||||||
I(dom, task->rust_sp < task->stk->limit);
|
|
||||||
|
|
||||||
dom->reap_dead_tasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
dom->log(rust_log::DOM, "finished main-loop (dom.rval = %d)", dom->rval);
|
|
||||||
rval = dom->rval;
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
command_line_args
|
command_line_args
|
||||||
{
|
{
|
||||||
@ -243,7 +196,7 @@ rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, char **argv)
|
|||||||
(uintptr_t)&main_args,
|
(uintptr_t)&main_args,
|
||||||
sizeof(main_args));
|
sizeof(main_args));
|
||||||
|
|
||||||
ret = rust_main_loop(&dom);
|
ret = dom.start_main_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__WIN32__)
|
#if !defined(__WIN32__)
|
||||||
|
@ -19,7 +19,7 @@ str_alloc(rust_task *task, size_t n_bytes)
|
|||||||
extern "C" CDECL rust_str*
|
extern "C" CDECL rust_str*
|
||||||
last_os_error(rust_task *task) {
|
last_os_error(rust_task *task) {
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::TASK, "last_os_error()");
|
task->log(rust_log::TASK, "last_os_error()");
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
LPTSTR buf;
|
LPTSTR buf;
|
||||||
@ -95,7 +95,7 @@ extern "C" CDECL rust_vec*
|
|||||||
vec_alloc(rust_task *task, type_desc *t, size_t n_elts)
|
vec_alloc(rust_task *task, type_desc *t, size_t n_elts)
|
||||||
{
|
{
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::MEM,
|
task->log(rust_log::MEM,
|
||||||
"vec_alloc %" PRIdPTR " elements of size %" PRIdPTR,
|
"vec_alloc %" PRIdPTR " elements of size %" PRIdPTR,
|
||||||
n_elts, t->size);
|
n_elts, t->size);
|
||||||
size_t fill = n_elts * t->size;
|
size_t fill = n_elts * t->size;
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
|
|
||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
#include "rust_chan.h"
|
#include "rust_chan.h"
|
||||||
|
|
||||||
rust_chan::rust_chan(rust_task *task, rust_port *port) :
|
rust_chan::rust_chan(rust_task *task, rust_port *port) :
|
||||||
task(task),
|
task(task), port(port), buffer(task->dom, port->unit_sz), token(this) {
|
||||||
port(port),
|
|
||||||
buffer(task->dom, port->unit_sz),
|
if (port) {
|
||||||
token(this)
|
|
||||||
{
|
|
||||||
if (port)
|
|
||||||
port->chans.push(this);
|
port->chans.push(this);
|
||||||
|
ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
task->log(rust_log::MEM | rust_log::COMM,
|
||||||
|
"new rust_chan(task=0x%" PRIxPTR
|
||||||
|
", port=0x%" PRIxPTR ") -> chan=0x%"
|
||||||
|
PRIxPTR, (uintptr_t) task, (uintptr_t) port, (uintptr_t) this);
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_chan::~rust_chan()
|
rust_chan::~rust_chan() {
|
||||||
{
|
|
||||||
if (port) {
|
if (port) {
|
||||||
if (token.pending())
|
if (token.pending())
|
||||||
token.withdraw();
|
token.withdraw();
|
||||||
port->chans.swapdel(this);
|
port->chans.swap_delete(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void rust_chan::disassociate() {
|
||||||
rust_chan::disassociate()
|
|
||||||
{
|
|
||||||
I(task->dom, port);
|
I(task->dom, port);
|
||||||
|
|
||||||
if (token.pending())
|
if (token.pending())
|
||||||
@ -31,4 +31,32 @@ rust_chan::disassociate()
|
|||||||
|
|
||||||
// Delete reference to the port/
|
// Delete reference to the port/
|
||||||
port = NULL;
|
port = NULL;
|
||||||
|
|
||||||
|
deref();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to transmit channel data to the associated port.
|
||||||
|
*/
|
||||||
|
int rust_chan::transmit() {
|
||||||
|
rust_dom *dom = task->dom;
|
||||||
|
|
||||||
|
// TODO: Figure out how and why the port would become null.
|
||||||
|
if (port == NULL) {
|
||||||
|
dom->log(rust_log::COMM, "invalid port, transmission incomplete");
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.is_empty()) {
|
||||||
|
dom->log(rust_log::COMM, "buffer is empty, transmission incomplete");
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(port->task->blocked_on(port)) {
|
||||||
|
buffer.dequeue(port->task->rendezvous_ptr);
|
||||||
|
port->task->wakeup(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ public:
|
|||||||
|
|
||||||
rust_task *task;
|
rust_task *task;
|
||||||
rust_port *port;
|
rust_port *port;
|
||||||
circ_buf buffer;
|
circular_buffer buffer;
|
||||||
size_t idx; // Index into port->chans.
|
size_t idx; // Index into port->chans.
|
||||||
|
|
||||||
// Token belonging to this chan, it will be placed into a port's
|
// Token belonging to this chan, it will be placed into a port's
|
||||||
@ -17,6 +17,8 @@ public:
|
|||||||
rust_token token;
|
rust_token token;
|
||||||
|
|
||||||
void disassociate();
|
void disassociate();
|
||||||
|
|
||||||
|
int transmit();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* RUST_CHAN_H */
|
#endif /* RUST_CHAN_H */
|
||||||
|
@ -10,109 +10,6 @@ rust_alarm::rust_alarm(rust_task *receiver) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Circular buffers.
|
|
||||||
|
|
||||||
circ_buf::circ_buf(rust_dom *dom, size_t unit_sz) :
|
|
||||||
dom(dom),
|
|
||||||
alloc(INIT_CIRC_BUF_UNITS * unit_sz),
|
|
||||||
unit_sz(unit_sz),
|
|
||||||
next(0),
|
|
||||||
unread(0),
|
|
||||||
data((uint8_t *)dom->calloc(alloc))
|
|
||||||
{
|
|
||||||
I(dom, unit_sz);
|
|
||||||
dom->log(rust_log::MEM|rust_log::COMM,
|
|
||||||
"new circ_buf(alloc=%d, unread=%d) -> circ_buf=0x%" PRIxPTR,
|
|
||||||
alloc, unread, this);
|
|
||||||
I(dom, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
circ_buf::~circ_buf()
|
|
||||||
{
|
|
||||||
dom->log(rust_log::MEM|rust_log::COMM,
|
|
||||||
"~circ_buf 0x%" PRIxPTR,
|
|
||||||
this);
|
|
||||||
I(dom, data);
|
|
||||||
// I(dom, unread == 0);
|
|
||||||
dom->free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
circ_buf::transfer(void *dst)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
uint8_t *d = (uint8_t *)dst;
|
|
||||||
I(dom, dst);
|
|
||||||
for (i = 0; i < unread; i += unit_sz)
|
|
||||||
memcpy(&d[i], &data[next + i % alloc], unit_sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
circ_buf::push(void *src)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
void *tmp;
|
|
||||||
|
|
||||||
I(dom, src);
|
|
||||||
I(dom, unread <= alloc);
|
|
||||||
|
|
||||||
/* Grow if necessary. */
|
|
||||||
if (unread == alloc) {
|
|
||||||
I(dom, alloc <= MAX_CIRC_BUF_SIZE);
|
|
||||||
tmp = dom->malloc(alloc << 1);
|
|
||||||
transfer(tmp);
|
|
||||||
alloc <<= 1;
|
|
||||||
dom->free(data);
|
|
||||||
data = (uint8_t *)tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
dom->log(rust_log::MEM|rust_log::COMM,
|
|
||||||
"circ buf push, unread=%d, alloc=%d, unit_sz=%d",
|
|
||||||
unread, alloc, unit_sz);
|
|
||||||
|
|
||||||
I(dom, unread < alloc);
|
|
||||||
I(dom, unread + unit_sz <= alloc);
|
|
||||||
|
|
||||||
i = (next + unread) % alloc;
|
|
||||||
memcpy(&data[i], src, unit_sz);
|
|
||||||
|
|
||||||
dom->log(rust_log::MEM|rust_log::COMM, "pushed data at index %d", i);
|
|
||||||
unread += unit_sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
circ_buf::shift(void *dst)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
void *tmp;
|
|
||||||
|
|
||||||
I(dom, dst);
|
|
||||||
I(dom, unit_sz > 0);
|
|
||||||
I(dom, unread >= unit_sz);
|
|
||||||
I(dom, unread <= alloc);
|
|
||||||
I(dom, data);
|
|
||||||
i = next;
|
|
||||||
memcpy(dst, &data[i], unit_sz);
|
|
||||||
dom->log(rust_log::MEM|rust_log::COMM, "shifted data from index %d", i);
|
|
||||||
unread -= unit_sz;
|
|
||||||
next += unit_sz;
|
|
||||||
I(dom, next <= alloc);
|
|
||||||
if (next == alloc)
|
|
||||||
next = 0;
|
|
||||||
|
|
||||||
/* Shrink if necessary. */
|
|
||||||
if (alloc >= INIT_CIRC_BUF_UNITS * unit_sz &&
|
|
||||||
unread <= alloc / 4) {
|
|
||||||
tmp = dom->malloc(alloc / 2);
|
|
||||||
transfer(tmp);
|
|
||||||
alloc >>= 1;
|
|
||||||
dom->free(data);
|
|
||||||
data = (uint8_t *)tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Ports.
|
// Ports.
|
||||||
|
|
||||||
rust_port::rust_port(rust_task *task, size_t unit_sz) :
|
rust_port::rust_port(rust_task *task, size_t unit_sz) :
|
||||||
@ -121,18 +18,16 @@ rust_port::rust_port(rust_task *task, size_t unit_sz) :
|
|||||||
writers(task->dom),
|
writers(task->dom),
|
||||||
chans(task->dom)
|
chans(task->dom)
|
||||||
{
|
{
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::MEM|rust_log::COMM,
|
||||||
dom->log(rust_log::MEM|rust_log::COMM,
|
"new rust_port(task=0x%" PRIxPTR ", unit_sz=%d) -> port=0x%"
|
||||||
"new rust_port(task=0x%" PRIxPTR ", unit_sz=%d) -> port=0x%"
|
PRIxPTR, (uintptr_t)task, unit_sz, (uintptr_t)this);
|
||||||
PRIxPTR, (uintptr_t)task, unit_sz, (uintptr_t)this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_port::~rust_port()
|
rust_port::~rust_port()
|
||||||
{
|
{
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::COMM|rust_log::MEM,
|
||||||
dom->log(rust_log::COMM|rust_log::MEM,
|
"~rust_port 0x%" PRIxPTR,
|
||||||
"~rust_port 0x%" PRIxPTR,
|
(uintptr_t)this);
|
||||||
(uintptr_t)this);
|
|
||||||
while (chans.length() > 0)
|
while (chans.length() > 0)
|
||||||
chans.pop()->disassociate();
|
chans.pop()->disassociate();
|
||||||
}
|
}
|
||||||
@ -182,7 +77,7 @@ rust_token::withdraw()
|
|||||||
|
|
||||||
if (task->blocked())
|
if (task->blocked())
|
||||||
task->wakeup(this); // must be blocked on us (or dead)
|
task->wakeup(this); // must be blocked on us (or dead)
|
||||||
port->writers.swapdel(this);
|
port->writers.swap_delete(this);
|
||||||
submitted = false;
|
submitted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ rust_crate_cache::flush() {
|
|||||||
if (s) {
|
if (s) {
|
||||||
dom->log(rust_log::CACHE,
|
dom->log(rust_log::CACHE,
|
||||||
"rust_crate_cache::flush() deref rust_sym %"
|
"rust_crate_cache::flush() deref rust_sym %"
|
||||||
PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
|
PRIdPTR " (rc=%" PRIdPTR ")", i, s->ref_count);
|
||||||
s->deref();
|
s->deref();
|
||||||
}
|
}
|
||||||
rust_syms[i] = NULL;
|
rust_syms[i] = NULL;
|
||||||
@ -262,7 +262,7 @@ rust_crate_cache::flush() {
|
|||||||
if (s) {
|
if (s) {
|
||||||
dom->log(rust_log::CACHE,
|
dom->log(rust_log::CACHE,
|
||||||
"rust_crate_cache::flush() deref c_sym %"
|
"rust_crate_cache::flush() deref c_sym %"
|
||||||
PRIdPTR " (rc=%" PRIdPTR ")", i, s->refcnt);
|
PRIdPTR " (rc=%" PRIdPTR ")", i, s->ref_count);
|
||||||
s->deref();
|
s->deref();
|
||||||
}
|
}
|
||||||
c_syms[i] = NULL;
|
c_syms[i] = NULL;
|
||||||
@ -272,7 +272,7 @@ rust_crate_cache::flush() {
|
|||||||
lib *l = libs[i];
|
lib *l = libs[i];
|
||||||
if (l) {
|
if (l) {
|
||||||
dom->log(rust_log::CACHE, "rust_crate_cache::flush() deref lib %"
|
dom->log(rust_log::CACHE, "rust_crate_cache::flush() deref lib %"
|
||||||
PRIdPTR " (rc=%" PRIdPTR ")", i, l->refcnt);
|
PRIdPTR " (rc=%" PRIdPTR ")", i, l->ref_count);
|
||||||
l->deref();
|
l->deref();
|
||||||
}
|
}
|
||||||
libs[i] = NULL;
|
libs[i] = NULL;
|
||||||
|
@ -4,6 +4,24 @@
|
|||||||
|
|
||||||
template class ptr_vec<rust_task>;
|
template class ptr_vec<rust_task>;
|
||||||
|
|
||||||
|
rust_message::rust_message(rust_dom *dom) : dom(dom) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void rust_message::process() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_task_message::kill_task_message(rust_dom *dom, rust_task *task) :
|
||||||
|
rust_message(dom), _task(task) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill_task_message::process() {
|
||||||
|
_task->ref_count--;
|
||||||
|
_task->kill();
|
||||||
|
}
|
||||||
|
|
||||||
rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) :
|
rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate) :
|
||||||
interrupt_flag(0),
|
interrupt_flag(0),
|
||||||
root_crate(root_crate),
|
root_crate(root_crate),
|
||||||
@ -80,6 +98,18 @@ rust_dom::activate(rust_task *task) {
|
|||||||
curr_task = NULL;
|
curr_task = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_dom::log(rust_task *task, uint32_t type_bits, char const *fmt, ...) {
|
||||||
|
char buf[256];
|
||||||
|
if (_log.is_tracing(type_bits)) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
_log.trace_ln(task, type_bits, buf);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rust_dom::log(uint32_t type_bits, char const *fmt, ...) {
|
rust_dom::log(uint32_t type_bits, char const *fmt, ...) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
@ -87,7 +117,7 @@ rust_dom::log(uint32_t type_bits, char const *fmt, ...) {
|
|||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
_log.trace_ln(type_bits, buf);
|
_log.trace_ln(NULL, type_bits, buf);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +219,7 @@ rust_dom::remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task)
|
|||||||
"removing task 0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR,
|
"removing task 0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR,
|
||||||
(uintptr_t)task, state_vec_name(v), (uintptr_t)v);
|
(uintptr_t)task, state_vec_name(v), (uintptr_t)v);
|
||||||
I(this, (*v)[task->idx] == task);
|
I(this, (*v)[task->idx] == task);
|
||||||
v->swapdel(task);
|
v->swap_delete(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
@ -203,25 +233,67 @@ rust_dom::state_vec_name(ptr_vec<rust_task> *v)
|
|||||||
return "dead";
|
return "dead";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete any dead tasks.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
rust_dom::reap_dead_tasks()
|
rust_dom::reap_dead_tasks() {
|
||||||
{
|
|
||||||
for (size_t i = 0; i < dead_tasks.length(); ) {
|
for (size_t i = 0; i < dead_tasks.length(); ) {
|
||||||
rust_task *t = dead_tasks[i];
|
rust_task *task = dead_tasks[i];
|
||||||
if (t == root_task || t->refcnt == 0) {
|
// log(rust_log::TASK, "dead task 0x%" PRIxPTR " with ref_count: %d",
|
||||||
I(this, !t->waiting_tasks.length());
|
// task, task->ref_count);
|
||||||
dead_tasks.swapdel(t);
|
if (task->ref_count == 0) {
|
||||||
|
I(this, !task->waiting_tasks.length());
|
||||||
|
dead_tasks.swap_delete(task);
|
||||||
log(rust_log::TASK,
|
log(rust_log::TASK,
|
||||||
"deleting unreferenced dead task 0x%" PRIxPTR, t);
|
"deleting unreferenced dead task 0x%" PRIxPTR, task);
|
||||||
delete t;
|
delete task;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues a message in this domain's incoming message queue. It's the
|
||||||
|
* responsibility of the receiver to free the message once it's processed.
|
||||||
|
*/
|
||||||
|
void rust_dom::send_message(rust_message *message) {
|
||||||
|
log(rust_log::COMM, "enqueueing message 0x%" PRIxPTR
|
||||||
|
" in queue 0x%" PRIxPTR,
|
||||||
|
message,
|
||||||
|
&_incoming_message_queue);
|
||||||
|
_incoming_message_queue.enqueue(message);
|
||||||
|
_incoming_message_pending.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drains and processes incoming pending messages.
|
||||||
|
*/
|
||||||
|
void rust_dom::drain_incoming_message_queue() {
|
||||||
|
rust_message *message;
|
||||||
|
while ((message = (rust_message *) _incoming_message_queue.dequeue())) {
|
||||||
|
log(rust_log::COMM, "read 0x%" PRIxPTR
|
||||||
|
" from queue 0x%" PRIxPTR,
|
||||||
|
message,
|
||||||
|
&_incoming_message_queue);
|
||||||
|
log(rust_log::COMM, "processing incoming message 0x%" PRIxPTR,
|
||||||
|
message);
|
||||||
|
message->process();
|
||||||
|
delete message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a running task for execution. Only running tasks can be
|
||||||
|
* activated. Blocked tasks have to be unblocked before they can be
|
||||||
|
* activated.
|
||||||
|
*
|
||||||
|
* Returns NULL if no tasks can be scheduled.
|
||||||
|
*/
|
||||||
rust_task *
|
rust_task *
|
||||||
rust_dom::sched()
|
rust_dom::schedule_task()
|
||||||
{
|
{
|
||||||
I(this, this);
|
I(this, this);
|
||||||
// FIXME: in the face of failing tasks, this is not always right.
|
// FIXME: in the face of failing tasks, this is not always right.
|
||||||
@ -231,11 +303,88 @@ rust_dom::sched()
|
|||||||
i %= running_tasks.length();
|
i %= running_tasks.length();
|
||||||
return (rust_task *)running_tasks[i];
|
return (rust_task *)running_tasks[i];
|
||||||
}
|
}
|
||||||
log(rust_log::DOM|rust_log::TASK,
|
// log(rust_log::DOM|rust_log::TASK, "no schedulable tasks");
|
||||||
"no schedulable tasks");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the main scheduler loop which performs task scheduling for this
|
||||||
|
* domain.
|
||||||
|
*
|
||||||
|
* Returns once no more tasks can be scheduled.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rust_dom::start_main_loop()
|
||||||
|
{
|
||||||
|
// Make sure someone is watching, to pull us out of infinite loops.
|
||||||
|
rust_timer timer(this);
|
||||||
|
|
||||||
|
log(rust_log::DOM, "running main-loop on domain 0x%" PRIxPTR, this);
|
||||||
|
logptr("exit-task glue", root_crate->get_exit_task_glue());
|
||||||
|
|
||||||
|
while (n_live_tasks() > 0) {
|
||||||
|
rust_task *scheduled_task = schedule_task();
|
||||||
|
|
||||||
|
// If we cannot schedule a task because all other live tasks
|
||||||
|
// are blocked, wait on a condition variable which is signaled
|
||||||
|
// if progress is made in other domains.
|
||||||
|
|
||||||
|
if (scheduled_task == NULL) {
|
||||||
|
log(rust_log::TASK,
|
||||||
|
"all tasks are blocked, waiting for progress ...");
|
||||||
|
_progress.wait();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
I(this, scheduled_task->running());
|
||||||
|
|
||||||
|
log(rust_log::TASK,
|
||||||
|
"activating task 0x%" PRIxPTR ", sp=x%" PRIxPTR,
|
||||||
|
(uintptr_t)scheduled_task, scheduled_task->rust_sp);
|
||||||
|
|
||||||
|
interrupt_flag = 0;
|
||||||
|
|
||||||
|
activate(scheduled_task);
|
||||||
|
|
||||||
|
log(rust_log::TASK,
|
||||||
|
"returned from task 0x%" PRIxPTR
|
||||||
|
" in state '%s', sp=0x%" PRIxPTR,
|
||||||
|
(uintptr_t)scheduled_task,
|
||||||
|
state_vec_name(scheduled_task->state),
|
||||||
|
scheduled_task->rust_sp);
|
||||||
|
|
||||||
|
I(this, scheduled_task->rust_sp >=
|
||||||
|
(uintptr_t) &scheduled_task->stk->data[0]);
|
||||||
|
I(this, scheduled_task->rust_sp < scheduled_task->stk->limit);
|
||||||
|
|
||||||
|
drain_incoming_message_queue();
|
||||||
|
|
||||||
|
reap_dead_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
log(rust_log::DOM, "terminated scheduler loop, reaping dead tasks ...");
|
||||||
|
|
||||||
|
while (dead_tasks.length() > 0) {
|
||||||
|
log(rust_log::DOM,
|
||||||
|
"waiting for %d dead tasks to become dereferenced ...",
|
||||||
|
dead_tasks.length());
|
||||||
|
|
||||||
|
log(rust_log::DOM,
|
||||||
|
"waiting for %" PRIxPTR, dead_tasks[0]);
|
||||||
|
|
||||||
|
if (_incoming_message_queue.is_empty()) {
|
||||||
|
_incoming_message_pending.wait();
|
||||||
|
} else {
|
||||||
|
drain_incoming_message_queue();
|
||||||
|
}
|
||||||
|
reap_dead_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
log(rust_log::DOM, "finished main-loop (dom.rval = %d)", rval);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
rust_crate_cache *
|
rust_crate_cache *
|
||||||
rust_dom::get_cache(rust_crate const *crate) {
|
rust_dom::get_cache(rust_crate const *crate) {
|
||||||
log(rust_log::CACHE,
|
log(rust_log::CACHE,
|
||||||
|
92
src/rt/rust_dom.h
Normal file
92
src/rt/rust_dom.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* rust_dom.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RUST_DOM_H
|
||||||
|
#define RUST_DOM_H
|
||||||
|
|
||||||
|
#include "sync/lock_free_queue.h"
|
||||||
|
|
||||||
|
class rust_message : public lock_free_queue_node,
|
||||||
|
public dom_owned<rust_message> {
|
||||||
|
public:
|
||||||
|
rust_dom *dom;
|
||||||
|
rust_message(rust_dom *dom);
|
||||||
|
virtual void process();
|
||||||
|
};
|
||||||
|
|
||||||
|
class kill_task_message : public rust_message {
|
||||||
|
rust_task *_task;
|
||||||
|
public:
|
||||||
|
kill_task_message(rust_dom *dom, rust_task *task);
|
||||||
|
void process();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rust_dom
|
||||||
|
{
|
||||||
|
// Fields known to the compiler:
|
||||||
|
uintptr_t interrupt_flag;
|
||||||
|
|
||||||
|
// Fields known only by the runtime:
|
||||||
|
|
||||||
|
// NB: the root crate must remain in memory until the root of the
|
||||||
|
// tree of domains exits. All domains within this tree have a
|
||||||
|
// copy of this root_crate value and use it for finding utility
|
||||||
|
// glue.
|
||||||
|
rust_crate const *root_crate;
|
||||||
|
rust_log _log;
|
||||||
|
rust_srv *srv;
|
||||||
|
ptr_vec<rust_task> running_tasks;
|
||||||
|
ptr_vec<rust_task> blocked_tasks;
|
||||||
|
ptr_vec<rust_task> dead_tasks;
|
||||||
|
ptr_vec<rust_crate_cache> caches;
|
||||||
|
randctx rctx;
|
||||||
|
rust_task *root_task;
|
||||||
|
rust_task *curr_task;
|
||||||
|
int rval;
|
||||||
|
|
||||||
|
condition_variable _progress;
|
||||||
|
|
||||||
|
// Incoming messages from other domains.
|
||||||
|
condition_variable _incoming_message_pending;
|
||||||
|
lock_free_queue _incoming_message_queue;
|
||||||
|
|
||||||
|
#ifndef __WIN32__
|
||||||
|
pthread_attr_t attr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rust_dom(rust_srv *srv, rust_crate const *root_crate);
|
||||||
|
~rust_dom();
|
||||||
|
|
||||||
|
void activate(rust_task *task);
|
||||||
|
void log(rust_task *task, uint32_t logbit, char const *fmt, ...);
|
||||||
|
void log(uint32_t logbit, char const *fmt, ...);
|
||||||
|
rust_log & get_log();
|
||||||
|
void logptr(char const *msg, uintptr_t ptrval);
|
||||||
|
template<typename T>
|
||||||
|
void logptr(char const *msg, T* ptrval);
|
||||||
|
void fail();
|
||||||
|
void *malloc(size_t sz);
|
||||||
|
void *calloc(size_t sz);
|
||||||
|
void *realloc(void *data, size_t sz);
|
||||||
|
void free(void *p);
|
||||||
|
|
||||||
|
void send_message(rust_message *message);
|
||||||
|
void drain_incoming_message_queue();
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
void win32_require(LPCTSTR fn, BOOL ok);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rust_crate_cache *get_cache(rust_crate const *crate);
|
||||||
|
size_t n_live_tasks();
|
||||||
|
void add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
||||||
|
void remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
||||||
|
const char *state_vec_name(ptr_vec<rust_task> *v);
|
||||||
|
|
||||||
|
void reap_dead_tasks();
|
||||||
|
rust_task *schedule_task();
|
||||||
|
int start_main_loop();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* RUST_DOM_H */
|
@ -5,6 +5,8 @@
|
|||||||
#define __STDC_CONSTANT_MACROS 1
|
#define __STDC_CONSTANT_MACROS 1
|
||||||
#define __STDC_FORMAT_MACROS 1
|
#define __STDC_FORMAT_MACROS 1
|
||||||
|
|
||||||
|
#define ERROR 0
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@ -36,13 +38,18 @@ extern "C" {
|
|||||||
#error "Platform not supported."
|
#error "Platform not supported."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "sync/condition_variable.h"
|
||||||
|
|
||||||
#ifndef __i386__
|
#ifndef __i386__
|
||||||
#error "Target CPU not supported."
|
#error "Target CPU not supported."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define I(dom, e) ((e) ? (void)0 : \
|
#define I(dom, e) ((e) ? (void)0 : \
|
||||||
(dom)->srv->fatal(#e, __FILE__, __LINE__))
|
(dom)->srv->fatal(#e, __FILE__, __LINE__))
|
||||||
|
|
||||||
|
#define A(dom, e, s) ((e) ? (void)0 : \
|
||||||
|
(dom)->srv->fatal(#e " : " #s, __FILE__, __LINE__))
|
||||||
|
|
||||||
struct rust_task;
|
struct rust_task;
|
||||||
struct rust_port;
|
struct rust_port;
|
||||||
class rust_chan;
|
class rust_chan;
|
||||||
@ -50,7 +57,7 @@ struct rust_token;
|
|||||||
struct rust_dom;
|
struct rust_dom;
|
||||||
class rust_crate;
|
class rust_crate;
|
||||||
class rust_crate_cache;
|
class rust_crate_cache;
|
||||||
class lockfree_queue;
|
// class lockfree_queue;
|
||||||
|
|
||||||
struct stk_seg;
|
struct stk_seg;
|
||||||
struct type_desc;
|
struct type_desc;
|
||||||
@ -66,14 +73,14 @@ template <typename T>
|
|||||||
struct
|
struct
|
||||||
rc_base
|
rc_base
|
||||||
{
|
{
|
||||||
size_t refcnt;
|
size_t ref_count;
|
||||||
|
|
||||||
void ref() {
|
void ref() {
|
||||||
++refcnt;
|
++ref_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deref() {
|
void deref() {
|
||||||
if (--refcnt == 0) {
|
if (--ref_count == 0) {
|
||||||
delete (T*)this;
|
delete (T*)this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,71 +129,29 @@ public:
|
|||||||
return fill;
|
return fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_empty() {
|
||||||
|
return fill == 0;
|
||||||
|
}
|
||||||
|
|
||||||
T *& operator[](size_t offset);
|
T *& operator[](size_t offset);
|
||||||
void push(T *p);
|
void push(T *p);
|
||||||
T *pop();
|
T *pop();
|
||||||
void trim(size_t fill);
|
void trim(size_t fill);
|
||||||
void swapdel(T* p);
|
void swap_delete(T* p);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
#include "rust_dom.h"
|
||||||
rust_dom
|
|
||||||
{
|
|
||||||
// Fields known to the compiler:
|
|
||||||
uintptr_t interrupt_flag;
|
|
||||||
|
|
||||||
// Fields known only by the runtime:
|
template <typename T> inline T
|
||||||
|
check_null(rust_dom *dom, T value, char const *expr,
|
||||||
|
char const *file, size_t line) {
|
||||||
|
if (value == NULL) {
|
||||||
|
dom->srv->fatal(expr, file, line);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// NB: the root crate must remain in memory until the root of the
|
#define CHECK_NULL(dom, e) (check_null(dom, e, #e, __FILE__, __LINE__))
|
||||||
// tree of domains exits. All domains within this tree have a
|
|
||||||
// copy of this root_crate value and use it for finding utility
|
|
||||||
// glue.
|
|
||||||
rust_crate const *root_crate;
|
|
||||||
rust_log _log;
|
|
||||||
rust_srv *srv;
|
|
||||||
// uint32_t logbits;
|
|
||||||
ptr_vec<rust_task> running_tasks;
|
|
||||||
ptr_vec<rust_task> blocked_tasks;
|
|
||||||
ptr_vec<rust_task> dead_tasks;
|
|
||||||
ptr_vec<rust_crate_cache> caches;
|
|
||||||
randctx rctx;
|
|
||||||
rust_task *root_task;
|
|
||||||
rust_task *curr_task;
|
|
||||||
int rval;
|
|
||||||
lockfree_queue *incoming; // incoming messages from other threads
|
|
||||||
|
|
||||||
#ifndef __WIN32__
|
|
||||||
pthread_attr_t attr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rust_dom(rust_srv *srv, rust_crate const *root_crate);
|
|
||||||
~rust_dom();
|
|
||||||
|
|
||||||
void activate(rust_task *task);
|
|
||||||
void log(uint32_t logbit, char const *fmt, ...);
|
|
||||||
rust_log & get_log();
|
|
||||||
void logptr(char const *msg, uintptr_t ptrval);
|
|
||||||
template<typename T>
|
|
||||||
void logptr(char const *msg, T* ptrval);
|
|
||||||
void fail();
|
|
||||||
void *malloc(size_t sz);
|
|
||||||
void *calloc(size_t sz);
|
|
||||||
void *realloc(void *data, size_t sz);
|
|
||||||
void free(void *p);
|
|
||||||
|
|
||||||
#ifdef __WIN32__
|
|
||||||
void win32_require(LPCTSTR fn, BOOL ok);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rust_crate_cache *get_cache(rust_crate const *crate);
|
|
||||||
size_t n_live_tasks();
|
|
||||||
void add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
|
||||||
void remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task);
|
|
||||||
const char *state_vec_name(ptr_vec<rust_task> *v);
|
|
||||||
|
|
||||||
void reap_dead_tasks();
|
|
||||||
rust_task *sched();
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void *operator new(size_t sz, void *mem) {
|
inline void *operator new(size_t sz, void *mem) {
|
||||||
return mem;
|
return mem;
|
||||||
@ -217,7 +182,7 @@ rust_timer
|
|||||||
// For now it's just the most basic "thread that can interrupt
|
// For now it's just the most basic "thread that can interrupt
|
||||||
// its associated domain-thread" device, so that we have
|
// its associated domain-thread" device, so that we have
|
||||||
// *some* form of task-preemption.
|
// *some* form of task-preemption.
|
||||||
rust_dom &dom;
|
rust_dom *dom;
|
||||||
uintptr_t exit_flag;
|
uintptr_t exit_flag;
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
@ -227,7 +192,7 @@ rust_timer
|
|||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rust_timer(rust_dom &dom);
|
rust_timer(rust_dom *dom);
|
||||||
~rust_timer();
|
~rust_timer();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -608,94 +573,8 @@ struct gc_alloc {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
#include "rust_proxy.h"
|
||||||
rust_task : public rc_base<rust_task>,
|
#include "rust_task.h"
|
||||||
public dom_owned<rust_task>,
|
|
||||||
public rust_cond
|
|
||||||
{
|
|
||||||
// Fields known to the compiler.
|
|
||||||
stk_seg *stk;
|
|
||||||
uintptr_t runtime_sp; // Runtime sp while task running.
|
|
||||||
uintptr_t rust_sp; // Saved sp when not running.
|
|
||||||
gc_alloc *gc_alloc_chain; // Linked list of GC allocations.
|
|
||||||
rust_dom *dom;
|
|
||||||
rust_crate_cache *cache;
|
|
||||||
|
|
||||||
// Fields known only to the runtime.
|
|
||||||
ptr_vec<rust_task> *state;
|
|
||||||
rust_cond *cond;
|
|
||||||
uintptr_t* dptr; // Rendezvous pointer for send/recv.
|
|
||||||
rust_task *supervisor; // Parent-link for failure propagation.
|
|
||||||
size_t idx;
|
|
||||||
size_t gc_alloc_thresh;
|
|
||||||
size_t gc_alloc_accum;
|
|
||||||
|
|
||||||
// Wait queue for tasks waiting for this task.
|
|
||||||
rust_wait_queue waiting_tasks;
|
|
||||||
rust_alarm alarm;
|
|
||||||
|
|
||||||
rust_task(rust_dom *dom,
|
|
||||||
rust_task *spawner);
|
|
||||||
~rust_task();
|
|
||||||
|
|
||||||
void start(uintptr_t exit_task_glue,
|
|
||||||
uintptr_t spawnee_fn,
|
|
||||||
uintptr_t args,
|
|
||||||
size_t callsz);
|
|
||||||
void grow(size_t n_frame_bytes);
|
|
||||||
bool running();
|
|
||||||
bool blocked();
|
|
||||||
bool blocked_on(rust_cond *cond);
|
|
||||||
bool dead();
|
|
||||||
|
|
||||||
void link_gc(gc_alloc *gcm);
|
|
||||||
void unlink_gc(gc_alloc *gcm);
|
|
||||||
void *malloc(size_t sz, type_desc *td=0);
|
|
||||||
void *realloc(void *data, size_t sz, bool gc_mem=false);
|
|
||||||
void free(void *p, bool gc_mem=false);
|
|
||||||
|
|
||||||
const char *state_str();
|
|
||||||
void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec);
|
|
||||||
|
|
||||||
void block(rust_cond *on);
|
|
||||||
void wakeup(rust_cond *from);
|
|
||||||
void die();
|
|
||||||
void unblock();
|
|
||||||
|
|
||||||
void check_active() { I(dom, dom->curr_task == this); }
|
|
||||||
void check_suspended() { I(dom, dom->curr_task != this); }
|
|
||||||
|
|
||||||
// Swap in some glue code to run when we have returned to the
|
|
||||||
// task's context (assuming we're the active task).
|
|
||||||
void run_after_return(size_t nargs, uintptr_t glue);
|
|
||||||
|
|
||||||
// Swap in some glue code to run when we're next activated
|
|
||||||
// (assuming we're the suspended task).
|
|
||||||
void run_on_resume(uintptr_t glue);
|
|
||||||
|
|
||||||
// Save callee-saved registers and return to the main loop.
|
|
||||||
void yield(size_t nargs);
|
|
||||||
|
|
||||||
// Fail this task (assuming caller-on-stack is different task).
|
|
||||||
void kill();
|
|
||||||
|
|
||||||
// Fail self, assuming caller-on-stack is this task.
|
|
||||||
void fail(size_t nargs);
|
|
||||||
|
|
||||||
// Run the gc glue on the task stack.
|
|
||||||
void gc(size_t nargs);
|
|
||||||
|
|
||||||
// Disconnect from our supervisor.
|
|
||||||
void unsupervise();
|
|
||||||
|
|
||||||
// Notify tasks waiting for us that we are about to die.
|
|
||||||
void notify_waiting_tasks();
|
|
||||||
|
|
||||||
uintptr_t get_fp();
|
|
||||||
uintptr_t get_previous_fp(uintptr_t fp);
|
|
||||||
frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
|
|
||||||
rust_crate_cache * get_crate_cache(rust_crate const *curr_crate);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rust_port : public rc_base<rust_port>,
|
struct rust_port : public rc_base<rust_port>,
|
||||||
public task_owned<rust_port>,
|
public task_owned<rust_port>,
|
||||||
@ -722,31 +601,29 @@ struct rust_token : public rust_cond {
|
|||||||
void withdraw();
|
void withdraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "circular_buffer.h"
|
||||||
|
|
||||||
struct circ_buf : public dom_owned<circ_buf> {
|
//struct circ_buf : public dom_owned<circ_buf> {
|
||||||
static const size_t INIT_CIRC_BUF_UNITS = 8;
|
// static const size_t INIT_CIRC_BUF_UNITS = 8;
|
||||||
static const size_t MAX_CIRC_BUF_SIZE = 1 << 24;
|
// static const size_t MAX_CIRC_BUF_SIZE = 1 << 24;
|
||||||
|
//
|
||||||
rust_dom *dom;
|
// rust_dom *dom;
|
||||||
size_t alloc;
|
// size_t alloc;
|
||||||
size_t unit_sz;
|
// size_t unit_sz;
|
||||||
size_t next;
|
// size_t next;
|
||||||
size_t unread;
|
// size_t unread;
|
||||||
uint8_t *data;
|
// uint8_t *data;
|
||||||
|
//
|
||||||
circ_buf(rust_dom *dom, size_t unit_sz);
|
// circ_buf(rust_dom *dom, size_t unit_sz);
|
||||||
~circ_buf();
|
// ~circ_buf();
|
||||||
|
//
|
||||||
void transfer(void *dst);
|
// void transfer(void *dst);
|
||||||
void push(void *src);
|
// void push(void *src);
|
||||||
void shift(void *dst);
|
// void shift(void *dst);
|
||||||
};
|
//};
|
||||||
|
|
||||||
#include "rust_chan.h"
|
#include "rust_chan.h"
|
||||||
|
|
||||||
int
|
|
||||||
rust_main_loop(rust_dom *dom);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: C++
|
// mode: C++
|
||||||
|
@ -4,9 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
|
#include "sync/spin_lock.h"
|
||||||
|
#include "util/array_list.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
static uint32_t read_type_bit_mask() {
|
static uint32_t
|
||||||
uint32_t bits = rust_log::ULOG | rust_log::ERR;
|
read_type_bit_mask() {
|
||||||
|
uint32_t bits = rust_log::ULOG | rust_log::ERR | rust_log::ALL;
|
||||||
char *env_str = getenv("RUST_LOG");
|
char *env_str = getenv("RUST_LOG");
|
||||||
if (env_str) {
|
if (env_str) {
|
||||||
bits = 0;
|
bits = 0;
|
||||||
@ -27,92 +31,167 @@ static uint32_t read_type_bit_mask() {
|
|||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_log::ansi_color rust_log::get_type_color(log_type type) {
|
rust_log::ansi_color
|
||||||
switch (type) {
|
get_type_color(rust_log::log_type type) {
|
||||||
case ERR:
|
rust_log::ansi_color color = rust_log::WHITE;
|
||||||
return rust_log::RED;
|
if (type & rust_log::ERR)
|
||||||
case UPCALL:
|
color = rust_log::RED;
|
||||||
return rust_log::GREEN;
|
if (type & rust_log::MEM)
|
||||||
case COMM:
|
color = rust_log::YELLOW;
|
||||||
return rust_log::MAGENTA;
|
if (type & rust_log::UPCALL)
|
||||||
case DOM:
|
color = rust_log::GREEN;
|
||||||
case TASK:
|
if (type & rust_log::COMM)
|
||||||
return rust_log::LIGHTTEAL;
|
color = rust_log::MAGENTA;
|
||||||
case MEM:
|
if (type & rust_log::DOM)
|
||||||
return rust_log::YELLOW;
|
color = rust_log::LIGHTTEAL;
|
||||||
default:
|
if (type & rust_log::TASK)
|
||||||
return rust_log::WHITE;
|
color = rust_log::LIGHTTEAL;
|
||||||
}
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * _foreground_colors[] = { "[30m", "[1;30m", "[37m",
|
static const char * _foreground_colors[] = { "[37m",
|
||||||
"[31m", "[1;31m", "[32m",
|
"[31m", "[1;31m",
|
||||||
"[1;32m", "[33m", "[33m",
|
"[32m", "[1;32m",
|
||||||
"[34m", "[1;34m", "[35m",
|
"[33m", "[1;33m",
|
||||||
"[1;35m", "[36m", "[1;36m" };
|
"[31m", "[1;31m",
|
||||||
|
"[35m", "[1;35m",
|
||||||
|
"[36m", "[1;36m" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes access to the underlying logging mechanism.
|
||||||
|
*/
|
||||||
|
static spin_lock _log_lock;
|
||||||
|
|
||||||
rust_log::rust_log(rust_srv *srv, rust_dom *dom) :
|
rust_log::rust_log(rust_srv *srv, rust_dom *dom) :
|
||||||
_srv(srv), _dom(dom), _type_bit_mask(read_type_bit_mask()),
|
_srv(srv),
|
||||||
_use_colors(getenv("RUST_COLOR_LOG")), _indent(0) {
|
_dom(dom),
|
||||||
|
_type_bit_mask(read_type_bit_mask()),
|
||||||
|
_use_colors(getenv("RUST_COLOR_LOG")),
|
||||||
|
_indent(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_log::~rust_log() {
|
rust_log::~rust_log() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rust_log::trace_ln(char *message) {
|
const uint16_t
|
||||||
char buffer[512];
|
hash(uintptr_t ptr) {
|
||||||
if (_use_colors) {
|
// Robert Jenkins' 32 bit integer hash function
|
||||||
snprintf(buffer, sizeof(buffer), "\x1b%s0x%08" PRIxPTR "\x1b[0m: ",
|
ptr = (ptr + 0x7ed55d16) + (ptr << 12);
|
||||||
_foreground_colors[1 + ((uintptr_t) _dom % 2687 % (LIGHTTEAL
|
ptr = (ptr ^ 0xc761c23c) ^ (ptr >> 19);
|
||||||
- 1))], (uintptr_t) _dom);
|
ptr = (ptr + 0x165667b1) + (ptr << 5);
|
||||||
} else {
|
ptr = (ptr + 0xd3a2646c) ^ (ptr << 9);
|
||||||
snprintf(buffer, sizeof(buffer), "0x%08" PRIxPTR ": ",
|
ptr = (ptr + 0xfd7046c5) + (ptr << 3);
|
||||||
(uintptr_t) _dom);
|
ptr = (ptr ^ 0xb55a4f09) ^ (ptr >> 16);
|
||||||
}
|
return (uint16_t) ptr;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < _indent; i++) {
|
const char *
|
||||||
strncat(buffer, "\t", sizeof(buffer) - strlen(buffer) - 1);
|
get_color(uintptr_t ptr) {
|
||||||
|
return _foreground_colors[hash(ptr) % rust_log::LIGHTTEAL];
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
copy_string(char *dst, const char *src, size_t length) {
|
||||||
|
return strncpy(dst, src, length) + length;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
append_string(char *buffer, const char *format, ...) {
|
||||||
|
if (buffer != NULL && format) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vsprintf(buffer + strlen(buffer), format, args);
|
||||||
|
va_end(args);
|
||||||
}
|
}
|
||||||
strncat(buffer, message, sizeof(buffer) - strlen(buffer) - 1);
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
append_string(char *buffer, rust_log::ansi_color color,
|
||||||
|
const char *format, ...) {
|
||||||
|
if (buffer != NULL && format) {
|
||||||
|
append_string(buffer, "\x1b%s", _foreground_colors[color]);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vsprintf(buffer + strlen(buffer), format, args);
|
||||||
|
va_end(args);
|
||||||
|
append_string(buffer, "\x1b[0m");
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_log::trace_ln(char *prefix, char *message) {
|
||||||
|
char buffer[1024] = "";
|
||||||
|
_log_lock.lock();
|
||||||
|
append_string(buffer, "%-34s", prefix);
|
||||||
|
for (uint32_t i = 0; i < _indent; i++) {
|
||||||
|
append_string(buffer, " ");
|
||||||
|
}
|
||||||
|
append_string(buffer, "%s", message);
|
||||||
_srv->log(buffer);
|
_srv->log(buffer);
|
||||||
|
_log_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_log::trace_ln(rust_task *task, char *message) {
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
uint32_t thread_id = 0;
|
||||||
|
#else
|
||||||
|
uint32_t thread_id = (uint32_t) pthread_self();
|
||||||
|
#endif
|
||||||
|
char prefix[1024] = "";
|
||||||
|
append_string(prefix, "0x%08" PRIxPTR ":0x%08" PRIxPTR ":",
|
||||||
|
thread_id, (uintptr_t) _dom);
|
||||||
|
if (task) {
|
||||||
|
append_string(prefix, "0x%08" PRIxPTR ":", (uintptr_t) task);
|
||||||
|
}
|
||||||
|
trace_ln(prefix, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traces a log message if the specified logging type is not filtered.
|
* Traces a log message if the specified logging type is not filtered.
|
||||||
*/
|
*/
|
||||||
void rust_log::trace_ln(uint32_t type_bits, char *message) {
|
void
|
||||||
trace_ln(get_type_color((rust_log::log_type) type_bits), type_bits,
|
rust_log::trace_ln(rust_task *task, uint32_t type_bits, char *message) {
|
||||||
message);
|
trace_ln(task, get_type_color((rust_log::log_type) type_bits),
|
||||||
|
type_bits, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traces a log message using the specified ANSI color code.
|
* Traces a log message using the specified ANSI color code.
|
||||||
*/
|
*/
|
||||||
void rust_log::trace_ln(ansi_color color, uint32_t type_bits, char *message) {
|
void
|
||||||
|
rust_log::trace_ln(rust_task *task, ansi_color color,
|
||||||
|
uint32_t type_bits, char *message) {
|
||||||
if (is_tracing(type_bits)) {
|
if (is_tracing(type_bits)) {
|
||||||
if (_use_colors) {
|
if (_use_colors) {
|
||||||
char buffer[512];
|
char buffer[512] = "";
|
||||||
snprintf(buffer, sizeof(buffer), "\x1b%s%s\x1b[0m",
|
append_string(buffer, color, "%s", message);
|
||||||
_foreground_colors[color], message);
|
trace_ln(task, buffer);
|
||||||
trace_ln(buffer);
|
|
||||||
} else {
|
} else {
|
||||||
trace_ln(message);
|
trace_ln(task, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rust_log::is_tracing(uint32_t type_bits) {
|
bool
|
||||||
|
rust_log::is_tracing(uint32_t type_bits) {
|
||||||
return type_bits & _type_bit_mask;
|
return type_bits & _type_bit_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rust_log::indent() {
|
void
|
||||||
|
rust_log::indent() {
|
||||||
_indent++;
|
_indent++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rust_log::outdent() {
|
void
|
||||||
|
rust_log::outdent() {
|
||||||
_indent--;
|
_indent--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rust_log::reset_indent(uint32_t indent) {
|
void
|
||||||
|
rust_log::reset_indent(uint32_t indent) {
|
||||||
_indent = indent;
|
_indent = indent;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
#ifndef RUST_LOG_H_
|
#ifndef RUST_LOG_H
|
||||||
#define RUST_LOG_H_
|
#define RUST_LOG_H
|
||||||
|
|
||||||
class rust_dom;
|
class rust_dom;
|
||||||
|
class rust_task;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class rust_log {
|
class rust_log {
|
||||||
rust_srv *_srv;
|
|
||||||
rust_dom *_dom;
|
|
||||||
uint32_t _type_bit_mask;
|
|
||||||
bool _use_colors;
|
|
||||||
uint32_t _indent;
|
|
||||||
void trace_ln(char *message);
|
|
||||||
public:
|
public:
|
||||||
rust_log(rust_srv *srv, rust_dom *dom);
|
rust_log(rust_srv *srv, rust_dom *dom);
|
||||||
virtual ~rust_log();
|
virtual ~rust_log();
|
||||||
|
|
||||||
enum ansi_color {
|
enum ansi_color {
|
||||||
BLACK,
|
|
||||||
GRAY,
|
|
||||||
WHITE,
|
WHITE,
|
||||||
RED,
|
RED,
|
||||||
LIGHTRED,
|
LIGHTRED,
|
||||||
@ -51,10 +47,19 @@ public:
|
|||||||
void indent();
|
void indent();
|
||||||
void outdent();
|
void outdent();
|
||||||
void reset_indent(uint32_t indent);
|
void reset_indent(uint32_t indent);
|
||||||
void trace_ln(uint32_t type_bits, char *message);
|
void trace_ln(char *prefix, char *message);
|
||||||
void trace_ln(ansi_color color, uint32_t type_bits, char *message);
|
void trace_ln(rust_task *task, uint32_t type_bits, char *message);
|
||||||
|
void trace_ln(rust_task *task, ansi_color color, uint32_t type_bits, char *message);
|
||||||
bool is_tracing(uint32_t type_bits);
|
bool is_tracing(uint32_t type_bits);
|
||||||
static ansi_color get_type_color(log_type type);
|
|
||||||
|
private:
|
||||||
|
rust_srv *_srv;
|
||||||
|
rust_dom *_dom;
|
||||||
|
uint32_t _type_bit_mask;
|
||||||
|
bool _use_labels;
|
||||||
|
bool _use_colors;
|
||||||
|
uint32_t _indent;
|
||||||
|
void trace_ln(rust_task *task, char *message);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* RUST_LOG_H_ */
|
#endif /* RUST_LOG_H */
|
||||||
|
31
src/rt/rust_proxy.h
Normal file
31
src/rt/rust_proxy.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* A proxy object is a wrapper around other Rust objects. One use of the proxy
|
||||||
|
* object is to mitigate access between tasks in different thread domains.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RUST_PROXY_H
|
||||||
|
#define RUST_PROXY_H
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
rust_proxy_delegate : public rc_base<T> {
|
||||||
|
protected:
|
||||||
|
T *_delegate;
|
||||||
|
public:
|
||||||
|
rust_proxy_delegate(T * delegate) : _delegate(delegate) {
|
||||||
|
}
|
||||||
|
T *delegate() { return _delegate; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
rust_proxy : public rust_proxy_delegate<T>,
|
||||||
|
public dom_owned<rust_proxy<T> > {
|
||||||
|
public:
|
||||||
|
rust_dom *dom;
|
||||||
|
rust_proxy(rust_dom *dom, T *delegate) :
|
||||||
|
rust_proxy_delegate<T> (delegate),
|
||||||
|
dom(dom) {
|
||||||
|
delegate->ref();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* RUST_PROXY_H */
|
@ -53,6 +53,7 @@ align_down(uintptr_t sp)
|
|||||||
|
|
||||||
|
|
||||||
rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
|
rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
|
||||||
|
rust_proxy_delegate<rust_task>(this),
|
||||||
stk(new_stk(dom, 0)),
|
stk(new_stk(dom, 0)),
|
||||||
runtime_sp(0),
|
runtime_sp(0),
|
||||||
rust_sp(stk->limit),
|
rust_sp(stk->limit),
|
||||||
@ -61,20 +62,24 @@ rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
|
|||||||
cache(NULL),
|
cache(NULL),
|
||||||
state(&dom->running_tasks),
|
state(&dom->running_tasks),
|
||||||
cond(NULL),
|
cond(NULL),
|
||||||
dptr(0),
|
|
||||||
supervisor(spawner),
|
supervisor(spawner),
|
||||||
idx(0),
|
idx(0),
|
||||||
waiting_tasks(dom),
|
waiting_tasks(dom),
|
||||||
|
rendezvous_ptr(0),
|
||||||
alarm(this)
|
alarm(this)
|
||||||
{
|
{
|
||||||
dom->logptr("new task", (uintptr_t)this);
|
dom->logptr("new task", (uintptr_t)this);
|
||||||
|
|
||||||
|
if (spawner == NULL) {
|
||||||
|
ref_count = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_task::~rust_task()
|
rust_task::~rust_task()
|
||||||
{
|
{
|
||||||
dom->log(rust_log::MEM|rust_log::TASK,
|
dom->log(rust_log::MEM|rust_log::TASK,
|
||||||
"~rust_task 0x%" PRIxPTR ", refcnt=%d",
|
"~rust_task 0x%" PRIxPTR ", refcnt=%d",
|
||||||
(uintptr_t)this, refcnt);
|
(uintptr_t)this, ref_count);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) {
|
for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) {
|
||||||
@ -98,8 +103,8 @@ rust_task::~rust_task()
|
|||||||
|
|
||||||
/* FIXME: tighten this up, there are some more
|
/* FIXME: tighten this up, there are some more
|
||||||
assertions that hold at task-lifecycle events. */
|
assertions that hold at task-lifecycle events. */
|
||||||
I(dom, refcnt == 0 ||
|
I(dom, ref_count == 0 ||
|
||||||
(refcnt == 1 && this == dom->root_task));
|
(ref_count == 1 && this == dom->root_task));
|
||||||
|
|
||||||
del_stk(dom, stk);
|
del_stk(dom, stk);
|
||||||
if (cache)
|
if (cache)
|
||||||
@ -275,9 +280,9 @@ rust_task::run_after_return(size_t nargs, uintptr_t glue)
|
|||||||
|
|
||||||
uintptr_t *retpc = ((uintptr_t *) sp) - 1;
|
uintptr_t *retpc = ((uintptr_t *) sp) - 1;
|
||||||
dom->log(rust_log::TASK|rust_log::MEM,
|
dom->log(rust_log::TASK|rust_log::MEM,
|
||||||
"run_after_return: overwriting retpc=0x%" PRIxPTR
|
"run_after_return: overwriting retpc=x%" PRIxPTR
|
||||||
" @ runtime_sp=0x%" PRIxPTR
|
" @ runtime_sp=x%" PRIxPTR
|
||||||
" with glue=0x%" PRIxPTR,
|
" with glue=x%" PRIxPTR,
|
||||||
*retpc, sp, glue);
|
*retpc, sp, glue);
|
||||||
|
|
||||||
// Move the current return address (which points into rust code)
|
// Move the current return address (which points into rust code)
|
||||||
@ -296,9 +301,9 @@ rust_task::run_on_resume(uintptr_t glue)
|
|||||||
uintptr_t* rsp = (uintptr_t*) rust_sp;
|
uintptr_t* rsp = (uintptr_t*) rust_sp;
|
||||||
rsp += n_callee_saves;
|
rsp += n_callee_saves;
|
||||||
dom->log(rust_log::TASK|rust_log::MEM,
|
dom->log(rust_log::TASK|rust_log::MEM,
|
||||||
"run_on_resume: overwriting retpc=0x%" PRIxPTR
|
"run_on_resume: overwriting retpc=x%" PRIxPTR
|
||||||
" @ rust_sp=0x%" PRIxPTR
|
" @ rust_sp=x%" PRIxPTR
|
||||||
" with glue=0x%" PRIxPTR,
|
" with glue=x%" PRIxPTR,
|
||||||
*rsp, rsp, glue);
|
*rsp, rsp, glue);
|
||||||
*rsp = glue;
|
*rsp = glue;
|
||||||
}
|
}
|
||||||
@ -306,8 +311,8 @@ rust_task::run_on_resume(uintptr_t glue)
|
|||||||
void
|
void
|
||||||
rust_task::yield(size_t nargs)
|
rust_task::yield(size_t nargs)
|
||||||
{
|
{
|
||||||
dom->log(rust_log::TASK,
|
log(rust_log::TASK,
|
||||||
"task 0x%" PRIxPTR " yielding", this);
|
"task 0x%" PRIxPTR " yielding", this);
|
||||||
run_after_return(nargs, dom->root_crate->get_yield_glue());
|
run_after_return(nargs, dom->root_crate->get_yield_glue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,11 +327,13 @@ rust_task::kill() {
|
|||||||
// Note the distinction here: kill() is when you're in an upcall
|
// Note the distinction here: kill() is when you're in an upcall
|
||||||
// from task A and want to force-fail task B, you do B->kill().
|
// from task A and want to force-fail task B, you do B->kill().
|
||||||
// If you want to fail yourself you do self->fail(upcall_nargs).
|
// If you want to fail yourself you do self->fail(upcall_nargs).
|
||||||
dom->log(rust_log::TASK, "killing task 0x%" PRIxPTR, this);
|
log(rust_log::TASK, "killing task 0x%" PRIxPTR, this);
|
||||||
// Unblock the task so it can unwind.
|
// Unblock the task so it can unwind.
|
||||||
unblock();
|
unblock();
|
||||||
|
|
||||||
if (this == dom->root_task)
|
if (this == dom->root_task)
|
||||||
dom->fail();
|
dom->fail();
|
||||||
|
|
||||||
run_on_resume(dom->root_crate->get_unwind_glue());
|
run_on_resume(dom->root_crate->get_unwind_glue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,9 +376,12 @@ void
|
|||||||
rust_task::notify_waiting_tasks()
|
rust_task::notify_waiting_tasks()
|
||||||
{
|
{
|
||||||
while (waiting_tasks.length() > 0) {
|
while (waiting_tasks.length() > 0) {
|
||||||
rust_task *t = waiting_tasks.pop()->receiver;
|
log(rust_log::ALL, "notify_waiting_tasks: %d",
|
||||||
if (!t->dead())
|
waiting_tasks.length());
|
||||||
t->wakeup(this);
|
rust_task *waiting_task = waiting_tasks.pop()->receiver;
|
||||||
|
if (!waiting_task->dead()) {
|
||||||
|
waiting_task->wakeup(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,6 +542,9 @@ void
|
|||||||
rust_task::wakeup(rust_cond *from)
|
rust_task::wakeup(rust_cond *from)
|
||||||
{
|
{
|
||||||
transition(&dom->blocked_tasks, &dom->running_tasks);
|
transition(&dom->blocked_tasks, &dom->running_tasks);
|
||||||
|
// TODO: Signaling every time the task is awaken is kind of silly,
|
||||||
|
// do this a nicer way.
|
||||||
|
dom->_progress.signal();
|
||||||
I(dom, cond == from);
|
I(dom, cond == from);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +578,18 @@ rust_task::get_crate_cache(rust_crate const *curr_crate)
|
|||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_task::log(uint32_t type_bits, char const *fmt, ...) {
|
||||||
|
char buf[256];
|
||||||
|
if (dom->get_log().is_tracing(type_bits)) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
dom->get_log().trace_ln(this, type_bits, buf);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: C++
|
// mode: C++
|
||||||
|
107
src/rt/rust_task.h
Normal file
107
src/rt/rust_task.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RUST_TASK_H
|
||||||
|
#define RUST_TASK_H
|
||||||
|
struct
|
||||||
|
rust_task : public rust_proxy_delegate<rust_task>,
|
||||||
|
public dom_owned<rust_task>,
|
||||||
|
public rust_cond
|
||||||
|
{
|
||||||
|
// Fields known to the compiler.
|
||||||
|
stk_seg *stk;
|
||||||
|
uintptr_t runtime_sp; // Runtime sp while task running.
|
||||||
|
uintptr_t rust_sp; // Saved sp when not running.
|
||||||
|
gc_alloc *gc_alloc_chain; // Linked list of GC allocations.
|
||||||
|
rust_dom *dom;
|
||||||
|
rust_crate_cache *cache;
|
||||||
|
|
||||||
|
// Fields known only to the runtime.
|
||||||
|
ptr_vec<rust_task> *state;
|
||||||
|
rust_cond *cond;
|
||||||
|
rust_task *supervisor; // Parent-link for failure propagation.
|
||||||
|
size_t idx;
|
||||||
|
size_t gc_alloc_thresh;
|
||||||
|
size_t gc_alloc_accum;
|
||||||
|
|
||||||
|
// Wait queue for tasks waiting for this task.
|
||||||
|
rust_wait_queue waiting_tasks;
|
||||||
|
|
||||||
|
// Rendezvous pointer for receiving data when blocked on a port. If we're
|
||||||
|
// trying to read data and no data is available on any incoming channel,
|
||||||
|
// we block on the port, and yield control to the scheduler. Since, we
|
||||||
|
// were not able to read anJything, we remember the location where the
|
||||||
|
// result should go in the rendezvous_ptr, and let the sender write to
|
||||||
|
// that location before waking us up.
|
||||||
|
uintptr_t* rendezvous_ptr;
|
||||||
|
|
||||||
|
rust_alarm alarm;
|
||||||
|
|
||||||
|
rust_task(rust_dom *dom,
|
||||||
|
rust_task *spawner);
|
||||||
|
~rust_task();
|
||||||
|
|
||||||
|
void start(uintptr_t exit_task_glue,
|
||||||
|
uintptr_t spawnee_fn,
|
||||||
|
uintptr_t args,
|
||||||
|
size_t callsz);
|
||||||
|
void grow(size_t n_frame_bytes);
|
||||||
|
bool running();
|
||||||
|
bool blocked();
|
||||||
|
bool blocked_on(rust_cond *cond);
|
||||||
|
bool dead();
|
||||||
|
|
||||||
|
void link_gc(gc_alloc *gcm);
|
||||||
|
void unlink_gc(gc_alloc *gcm);
|
||||||
|
void *malloc(size_t sz, type_desc *td=0);
|
||||||
|
void *realloc(void *data, size_t sz, bool gc_mem=false);
|
||||||
|
void free(void *p, bool gc_mem=false);
|
||||||
|
|
||||||
|
const char *state_str();
|
||||||
|
void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec);
|
||||||
|
|
||||||
|
void block(rust_cond *on);
|
||||||
|
void wakeup(rust_cond *from);
|
||||||
|
void die();
|
||||||
|
void unblock();
|
||||||
|
|
||||||
|
void check_active() { I(dom, dom->curr_task == this); }
|
||||||
|
void check_suspended() { I(dom, dom->curr_task != this); }
|
||||||
|
|
||||||
|
void log(uint32_t type_bits, char const *fmt, ...);
|
||||||
|
|
||||||
|
// Swap in some glue code to run when we have returned to the
|
||||||
|
// task's context (assuming we're the active task).
|
||||||
|
void run_after_return(size_t nargs, uintptr_t glue);
|
||||||
|
|
||||||
|
// Swap in some glue code to run when we're next activated
|
||||||
|
// (assuming we're the suspended task).
|
||||||
|
void run_on_resume(uintptr_t glue);
|
||||||
|
|
||||||
|
// Save callee-saved registers and return to the main loop.
|
||||||
|
void yield(size_t nargs);
|
||||||
|
|
||||||
|
// Fail this task (assuming caller-on-stack is different task).
|
||||||
|
void kill();
|
||||||
|
|
||||||
|
// Fail self, assuming caller-on-stack is this task.
|
||||||
|
void fail(size_t nargs);
|
||||||
|
|
||||||
|
// Run the gc glue on the task stack.
|
||||||
|
void gc(size_t nargs);
|
||||||
|
|
||||||
|
// Disconnect from our supervisor.
|
||||||
|
void unsupervise();
|
||||||
|
|
||||||
|
// Notify tasks waiting for us that we are about to die.
|
||||||
|
void notify_waiting_tasks();
|
||||||
|
|
||||||
|
uintptr_t get_fp();
|
||||||
|
uintptr_t get_previous_fp(uintptr_t fp);
|
||||||
|
frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
|
||||||
|
rust_crate_cache * get_crate_cache(rust_crate const *curr_crate);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* RUST_TASK_H */
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
#include "valgrind.h"
|
#include "valgrind.h"
|
||||||
|
|
||||||
@ -27,12 +26,11 @@ static void *
|
|||||||
#else
|
#else
|
||||||
#error "Platform not supported"
|
#error "Platform not supported"
|
||||||
#endif
|
#endif
|
||||||
timer_loop(void *ptr)
|
timer_loop(void *ptr) {
|
||||||
{
|
|
||||||
// We were handed the rust_timer that owns us.
|
// We were handed the rust_timer that owns us.
|
||||||
rust_timer *timer = (rust_timer *)ptr;
|
rust_timer *timer = (rust_timer *)ptr;
|
||||||
rust_dom &dom = timer->dom;
|
rust_dom *dom = timer->dom;
|
||||||
dom.log(rust_log::TIMER, "in timer 0x%" PRIxPTR, (uintptr_t)timer);
|
dom->log(rust_log::TIMER, "in timer 0x%" PRIxPTR, (uintptr_t)timer);
|
||||||
size_t ms = TIME_SLICE_IN_MS;
|
size_t ms = TIME_SLICE_IN_MS;
|
||||||
if (!RUNNING_ON_VALGRIND)
|
if (!RUNNING_ON_VALGRIND)
|
||||||
ms = 1;
|
ms = 1;
|
||||||
@ -43,12 +41,10 @@ timer_loop(void *ptr)
|
|||||||
#else
|
#else
|
||||||
usleep(ms * 1000);
|
usleep(ms * 1000);
|
||||||
#endif
|
#endif
|
||||||
dom.log(rust_log::TIMER,
|
dom->log(rust_log::TIMER, "timer 0x%" PRIxPTR
|
||||||
"timer 0x%" PRIxPTR
|
" interrupting domain 0x%" PRIxPTR, (uintptr_t) timer,
|
||||||
" interrupting domain 0x%" PRIxPTR,
|
(uintptr_t) dom);
|
||||||
(uintptr_t)timer,
|
dom->interrupt_flag = 1;
|
||||||
(uintptr_t)&dom);
|
|
||||||
dom.interrupt_flag = 1;
|
|
||||||
}
|
}
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
@ -58,10 +54,9 @@ timer_loop(void *ptr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rust_timer::rust_timer(rust_dom *dom) :
|
||||||
rust_timer::rust_timer(rust_dom &dom) : dom(dom), exit_flag(0)
|
dom(dom), exit_flag(0) {
|
||||||
{
|
dom->log(rust_log::TIMER, "creating timer for domain 0x%" PRIxPTR, dom);
|
||||||
dom.log(rust_log::TIMER, "creating timer for domain 0x%" PRIxPTR, &dom);
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
thread = CreateThread(NULL, 0, timer_loop, this, 0, NULL);
|
thread = CreateThread(NULL, 0, timer_loop, this, 0, NULL);
|
||||||
dom.win32_require("CreateThread", thread != NULL);
|
dom.win32_require("CreateThread", thread != NULL);
|
||||||
@ -76,13 +71,11 @@ rust_timer::rust_timer(rust_dom &dom) : dom(dom), exit_flag(0)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
rust_timer::~rust_timer()
|
rust_timer::~rust_timer() {
|
||||||
{
|
|
||||||
exit_flag = 1;
|
exit_flag = 1;
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
dom.win32_require("WaitForSingleObject",
|
dom->win32_require("WaitForSingleObject",
|
||||||
WaitForSingleObject(thread, INFINITE)
|
WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0);
|
||||||
== WAIT_OBJECT_0);
|
|
||||||
#else
|
#else
|
||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,127 +1,120 @@
|
|||||||
|
|
||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
|
|
||||||
|
|
||||||
// Upcalls.
|
// Upcalls.
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define LOG_UPCALL_ENTRY(task) \
|
#define LOG_UPCALL_ENTRY(task) \
|
||||||
(task)->dom->get_log().reset_indent(0); \
|
(task)->dom->get_log().reset_indent(0); \
|
||||||
(task)->dom->log(rust_log::UPCALL, \
|
(task)->log(rust_log::UPCALL, \
|
||||||
"upcall task: 0x%" PRIxPTR \
|
"> UPCALL %s - task: 0x%" PRIxPTR \
|
||||||
" retpc: 0x%" PRIxPTR, \
|
" retpc: x%" PRIxPTR, \
|
||||||
(task), __builtin_return_address(0)); \
|
__FUNCTION__, \
|
||||||
|
(task), __builtin_return_address(0)); \
|
||||||
(task)->dom->get_log().indent();
|
(task)->dom->get_log().indent();
|
||||||
#else
|
#else
|
||||||
#define LOG_UPCALL_ENTRY(task) \
|
#define LOG_UPCALL_ENTRY(task) \
|
||||||
(task)->dom->get_log().reset_indent(0); \
|
(task)->dom->get_log().reset_indent(0); \
|
||||||
(task)->dom->log(rust_log::UPCALL, \
|
(task)->log(rust_log::UPCALL, \
|
||||||
"upcall task: 0x%" PRIxPTR (task)); \
|
"> UPCALL task: x%" PRIxPTR (task)); \
|
||||||
(task)->dom->get_log().indent();
|
(task)->dom->get_log().indent();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" CDECL char const *str_buf(rust_task *task, rust_str *s);
|
extern "C" CDECL char const *str_buf(rust_task *task, rust_str *s);
|
||||||
|
|
||||||
extern "C" void
|
inline bool
|
||||||
upcall_grow_task(rust_task *task, size_t n_frame_bytes)
|
requires_message_passing(rust_task *sender, rust_task *receiver) {
|
||||||
{
|
return sender->dom != receiver->dom;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void upcall_grow_task(rust_task *task, size_t n_frame_bytes) {
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->grow(n_frame_bytes);
|
task->grow(n_frame_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_log_int(rust_task *task, int32_t i) {
|
||||||
upcall_log_int(rust_task *task, int32_t i)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::ULOG,
|
task->log(rust_log::UPCALL | rust_log::ULOG,
|
||||||
"upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')",
|
"upcall log_int(0x%" PRIx32 " = %" PRId32 " = '%c')", i, i,
|
||||||
i, i, (char)i);
|
(char) i);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_log_str(rust_task *task, rust_str *str) {
|
||||||
upcall_log_str(rust_task *task, rust_str *str)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
const char *c = str_buf(task, str);
|
const char *c = str_buf(task, str);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::ULOG,
|
task->log(rust_log::UPCALL | rust_log::ULOG, "upcall log_str(\"%s\")", c);
|
||||||
"upcall log_str(\"%s\")",
|
|
||||||
c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_trace_word(rust_task *task, uintptr_t i) {
|
||||||
upcall_trace_word(rust_task *task, uintptr_t i)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::TRACE,
|
task->log(rust_log::UPCALL | rust_log::TRACE, "trace: 0x%" PRIxPTR "", i,
|
||||||
"trace: 0x%" PRIxPTR "",
|
i, (char) i);
|
||||||
i, i, (char)i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_trace_str(rust_task *task, char const *c) {
|
||||||
upcall_trace_str(rust_task *task, char const *c)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::TRACE,
|
task->log(rust_log::UPCALL | rust_log::TRACE, "trace: %s", c);
|
||||||
"trace: %s",
|
|
||||||
c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_port*
|
extern "C" CDECL rust_port*
|
||||||
upcall_new_port(rust_task *task, size_t unit_sz)
|
upcall_new_port(rust_task *task, size_t unit_sz) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM,
|
||||||
"upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)",
|
"upcall_new_port(task=0x%" PRIxPTR ", unit_sz=%d)",
|
||||||
(uintptr_t)task, unit_sz);
|
(uintptr_t) task, unit_sz);
|
||||||
return new (dom) rust_port(task, unit_sz);
|
return new (dom) rust_port(task, unit_sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_del_port(rust_task *task, rust_port *port) {
|
||||||
upcall_del_port(rust_task *task, rust_port *port)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM,
|
||||||
"upcall del_port(0x%" PRIxPTR ")", (uintptr_t)port);
|
"upcall del_port(0x%" PRIxPTR ")", (uintptr_t) port);
|
||||||
I(task->dom, !port->refcnt);
|
I(task->dom, !port->ref_count);
|
||||||
delete port;
|
delete port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new channel, pointed to a specified port.
|
||||||
|
*/
|
||||||
extern "C" CDECL rust_chan*
|
extern "C" CDECL rust_chan*
|
||||||
upcall_new_chan(rust_task *task, rust_port *port)
|
upcall_new_chan(rust_task *task, rust_port *port) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM,
|
||||||
"upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
|
"upcall_new_chan(task=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
|
||||||
(uintptr_t)task, port);
|
(uintptr_t) task, port);
|
||||||
I(dom, port);
|
I(dom, port);
|
||||||
return new (dom) rust_chan(task, port);
|
return new (dom) rust_chan(task, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
/**
|
||||||
upcall_del_chan(rust_task *task, rust_chan *chan)
|
* Called whenever the channel's ref count drops to zero.
|
||||||
{
|
*/
|
||||||
|
extern "C" CDECL void upcall_del_chan(rust_task *task, rust_chan *chan) {
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM,
|
||||||
"upcall del_chan(0x%" PRIxPTR ")", (uintptr_t)chan);
|
"upcall del_chan(0x%" PRIxPTR ")", (uintptr_t) chan);
|
||||||
I(dom, !chan->refcnt);
|
I(dom, !chan->ref_count);
|
||||||
delete chan;
|
delete chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a channel and stores it in the spawnee's domain. Each spawned task
|
||||||
|
* has it's own copy of the channel.
|
||||||
|
*/
|
||||||
extern "C" CDECL rust_chan *
|
extern "C" CDECL rust_chan *
|
||||||
upcall_clone_chan(rust_task *task, rust_task *owner, rust_chan *chan)
|
upcall_clone_chan(rust_task *task,
|
||||||
{
|
rust_proxy_delegate<rust_task> *spawnee_proxy,
|
||||||
|
rust_chan *chan) {
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_task *spawnee = spawnee_proxy->delegate();
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::COMM,
|
task->log(rust_log::UPCALL | rust_log::MEM | rust_log::COMM,
|
||||||
"upcall clone_chan(owner 0x%" PRIxPTR ", chan 0x%" PRIxPTR ")",
|
"spawnee: 0x%" PRIxPTR ", chan: 0x%" PRIxPTR,
|
||||||
(uintptr_t)owner, (uintptr_t)chan);
|
(uintptr_t) spawnee, (uintptr_t) chan);
|
||||||
return new (owner->dom) rust_chan(owner, chan->port);
|
return new (spawnee->dom) rust_chan(spawnee, chan->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Buffering protocol:
|
* Buffering protocol:
|
||||||
*
|
*
|
||||||
@ -143,74 +136,64 @@ upcall_clone_chan(rust_task *task, rust_task *owner, rust_chan *chan)
|
|||||||
* - Set blocked writer to running
|
* - 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;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// circular_buffer *buf = &src->buffer;
|
||||||
|
// if (buf->is_empty()) {
|
||||||
|
// 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->dequeue(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;
|
||||||
|
//}
|
||||||
|
|
||||||
static int
|
extern "C" CDECL void upcall_yield(rust_task *task) {
|
||||||
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);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::UPCALL | rust_log::COMM, "upcall yield()");
|
||||||
dom->log(rust_log::UPCALL|rust_log::COMM, "upcall yield()");
|
|
||||||
task->yield(1);
|
task->yield(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void upcall_join(rust_task *task,
|
||||||
upcall_join(rust_task *task, rust_task *other)
|
rust_proxy_delegate<rust_task> *proxy) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::UPCALL | rust_log::COMM,
|
||||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
"join proxy 0x%" PRIxPTR " -> task = 0x%" PRIxPTR,
|
||||||
"upcall join(other=0x%" PRIxPTR ")",
|
proxy, proxy->delegate());
|
||||||
(uintptr_t)other);
|
|
||||||
|
|
||||||
// If the other task is already dying, we dont have to wait for it.
|
rust_task *other = proxy->delegate();
|
||||||
|
|
||||||
|
// If the other task is already dying, we don't have to wait for it.
|
||||||
if (!other->dead()) {
|
if (!other->dead()) {
|
||||||
other->waiting_tasks.push(&task->alarm);
|
other->waiting_tasks.push(&task->alarm);
|
||||||
task->block(other);
|
task->block(other);
|
||||||
@ -218,106 +201,91 @@ upcall_join(rust_task *task, rust_task *other)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an chunk of data along the specified channel.
|
||||||
|
*
|
||||||
|
* sptr: pointer to a chunk of data to send
|
||||||
|
*/
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void
|
||||||
upcall_send(rust_task *task, rust_chan *chan, void *sptr)
|
upcall_send(rust_task *task, rust_chan *chan, void *sptr) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::UPCALL | rust_log::COMM,
|
||||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
"chan: 0x%" PRIxPTR ", sptr: 0x%" PRIxPTR ", size: %d",
|
||||||
"upcall send(chan=0x%" PRIxPTR ", sptr=0x%" PRIxPTR ")",
|
(uintptr_t) chan, (uintptr_t) sptr, chan->port->unit_sz);
|
||||||
(uintptr_t)chan,
|
|
||||||
(uintptr_t)sptr);
|
|
||||||
|
|
||||||
I(dom, chan);
|
chan->buffer.enqueue(sptr);
|
||||||
I(dom, sptr);
|
chan->transmit();
|
||||||
|
task->log(rust_log::COMM, "=== WROTE DATA ===>");
|
||||||
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
|
extern "C" CDECL void
|
||||||
upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port)
|
upcall_recv(rust_task *task, uintptr_t *dptr, rust_port *port) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::UPCALL | rust_log::COMM,
|
||||||
dom->log(rust_log::UPCALL|rust_log::COMM,
|
"port: 0x%" PRIxPTR ", dptr: 0x%" PRIxPTR
|
||||||
"upcall recv(dptr=0x%" PRIxPTR ", port=0x%" PRIxPTR ")",
|
", size: 0x%" PRIxPTR ", chan_no: %d",
|
||||||
(uintptr_t)dptr,
|
(uintptr_t) port, (uintptr_t) dptr, port->unit_sz,
|
||||||
(uintptr_t)port);
|
port->chans.length());
|
||||||
|
|
||||||
I(dom, port);
|
for (uint32_t i = 0; i < port->chans.length(); i++) {
|
||||||
I(dom, port->task);
|
rust_chan *chan = port->chans[i];
|
||||||
I(dom, task);
|
if (chan->buffer.is_empty() == false) {
|
||||||
I(dom, port->task == task);
|
chan->buffer.dequeue(dptr);
|
||||||
|
task->log(rust_log::COMM, "<=== READ DATA ===");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No data was buffered on any incoming channel, so block this task
|
||||||
|
// on the port. Remember the rendezvous location so that any sender
|
||||||
|
// task can write to it before waking up this task.
|
||||||
|
|
||||||
|
task->rendezvous_ptr = dptr;
|
||||||
task->block(port);
|
task->block(port);
|
||||||
|
task->yield(3);
|
||||||
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
|
extern "C" CDECL void upcall_fail(rust_task *task, char const *expr,
|
||||||
upcall_fail(rust_task *task, char const *expr, char const *file, size_t line)
|
char const *file, size_t line) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::ERR,
|
task->log(rust_log::UPCALL | rust_log::ERR,
|
||||||
"upcall fail '%s', %s:%" PRIdPTR,
|
"upcall fail '%s', %s:%" PRIdPTR, expr, file, line);
|
||||||
expr, file, line);
|
|
||||||
task->fail(4);
|
task->fail(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever a task's ref count drops to zero.
|
||||||
|
*/
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void
|
||||||
upcall_kill(rust_task *task, rust_task *target)
|
upcall_kill(rust_task *task, rust_proxy_delegate<rust_task> *target_proxy) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
task->dom->log(rust_log::UPCALL|rust_log::TASK,
|
rust_task *target_task = target_proxy->delegate();
|
||||||
"upcall kill target=0x%" PRIxPTR, target);
|
if (target_proxy != target_task) {
|
||||||
target->kill();
|
task->dom->free(target_proxy);
|
||||||
|
}
|
||||||
|
task->log(rust_log::UPCALL | rust_log::TASK,
|
||||||
|
"kill task 0x%" PRIxPTR ", ref count %d",
|
||||||
|
target_task,
|
||||||
|
target_task->ref_count);
|
||||||
|
|
||||||
|
if (requires_message_passing(task, target_task)) {
|
||||||
|
rust_dom *target_domain = target_task->dom;
|
||||||
|
target_domain->send_message(
|
||||||
|
new (target_domain)
|
||||||
|
kill_task_message(target_domain, target_task));
|
||||||
|
} else {
|
||||||
|
target_task->kill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the exit glue when the task terminates.
|
||||||
|
*/
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void
|
||||||
upcall_exit(rust_task *task)
|
upcall_exit(rust_task *task) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
|
task->log(rust_log::UPCALL | rust_log::TASK,
|
||||||
rust_dom *dom = task->dom;
|
"task ref_count: %d", task->ref_count);
|
||||||
dom->log(rust_log::UPCALL|rust_log::TASK, "upcall exit");
|
|
||||||
task->die();
|
task->die();
|
||||||
task->notify_waiting_tasks();
|
task->notify_waiting_tasks();
|
||||||
task->yield(1);
|
task->yield(1);
|
||||||
@ -341,11 +309,13 @@ upcall_malloc(rust_task *task, size_t nbytes, type_desc *td)
|
|||||||
return (uintptr_t) p;
|
return (uintptr_t) p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever an object's ref count drops to zero.
|
||||||
|
*/
|
||||||
extern "C" CDECL void
|
extern "C" CDECL void
|
||||||
upcall_free(rust_task *task, void* ptr, uintptr_t is_gc)
|
upcall_free(rust_task *task, void* ptr, uintptr_t is_gc)
|
||||||
{
|
{
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
|
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
dom->log(rust_log::UPCALL|rust_log::MEM,
|
||||||
"upcall free(0x%" PRIxPTR ")",
|
"upcall free(0x%" PRIxPTR ")",
|
||||||
@ -371,22 +341,19 @@ upcall_mark(rust_task *task, void* ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_str *
|
extern "C" CDECL rust_str *
|
||||||
upcall_new_str(rust_task *task, char const *s, size_t fill)
|
upcall_new_str(rust_task *task, char const *s, size_t fill) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
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);
|
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
|
||||||
void *mem = dom->malloc(alloc);
|
void *mem = dom->malloc(alloc);
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
task->fail(3);
|
task->fail(3);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rust_str *st = new (mem) rust_str(dom, alloc, fill, (uint8_t const *)s);
|
rust_str *st = new (mem) rust_str(dom, alloc, fill, (uint8_t const *) s);
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
task->log(rust_log::UPCALL | rust_log::MEM,
|
||||||
"upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
|
"upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
|
||||||
s, fill, st);
|
s, fill, st);
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,34 +372,32 @@ upcall_new_vec(rust_task *task, size_t fill, type_desc *td)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rust_vec *v = new (mem) rust_vec(dom, alloc, 0, NULL);
|
rust_vec *v = new (mem) rust_vec(dom, alloc, 0, NULL);
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
task->log(rust_log::UPCALL | rust_log::MEM,
|
||||||
"upcall new_vec(%" PRIdPTR ") = 0x%" PRIxPTR,
|
"upcall new_vec(%" PRIdPTR ") = 0x%" PRIxPTR, fill, v);
|
||||||
fill, v);
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" CDECL rust_str *
|
extern "C" CDECL rust_str *
|
||||||
upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes, uintptr_t is_gc)
|
upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes, uintptr_t is_gc)
|
||||||
{
|
{
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM,
|
task->log(rust_log::UPCALL|rust_log::MEM,
|
||||||
"upcall vec_grow(%" PRIxPTR ", %" PRIdPTR
|
"upcall vec_grow(%" PRIxPTR ", %" PRIdPTR
|
||||||
"), alloc=%" PRIdPTR ", fill=%" PRIdPTR,
|
"), alloc=%" PRIdPTR ", fill=%" PRIdPTR,
|
||||||
v, n_bytes, v->alloc, v->fill);
|
v, n_bytes, v->alloc, v->fill);
|
||||||
size_t alloc = next_power_of_two(sizeof(rust_vec) + v->fill + n_bytes);
|
size_t alloc = next_power_of_two(sizeof(rust_vec) + v->fill + n_bytes);
|
||||||
if (v->refcnt == 1) {
|
if (v->ref_count == 1) {
|
||||||
|
|
||||||
// Fastest path: already large enough.
|
// Fastest path: already large enough.
|
||||||
if (v->alloc >= alloc) {
|
if (v->alloc >= alloc) {
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM, "no-growth path");
|
task->log(rust_log::UPCALL | rust_log::MEM, "no-growth path");
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second-fastest path: can at least realloc.
|
// Second-fastest path: can at least realloc.
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM, "realloc path");
|
task->log(rust_log::UPCALL | rust_log::MEM, "realloc path");
|
||||||
v = (rust_vec*)dom->realloc(v, alloc);
|
v = (rust_vec*) dom->realloc(v, alloc);
|
||||||
if (!v) {
|
if (!v) {
|
||||||
task->fail(4);
|
task->fail(4);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -441,7 +406,7 @@ upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes, uintptr_t is_gc)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Slowest path: make a new vec.
|
// Slowest path: make a new vec.
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM, "new vec path");
|
task->log(rust_log::UPCALL | rust_log::MEM, "new vec path");
|
||||||
void *mem = dom->malloc(alloc);
|
void *mem = dom->malloc(alloc);
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
task->fail(4);
|
task->fail(4);
|
||||||
@ -454,121 +419,100 @@ upcall_vec_grow(rust_task *task, rust_vec *v, size_t n_bytes, uintptr_t is_gc)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static rust_crate_cache::c_sym *
|
static rust_crate_cache::c_sym *
|
||||||
fetch_c_sym(rust_task *task,
|
fetch_c_sym(rust_task *task, rust_crate const *curr_crate, size_t lib_num,
|
||||||
rust_crate const *curr_crate,
|
size_t c_sym_num, char const *library, char const *symbol) {
|
||||||
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 *cache = task->get_crate_cache(curr_crate);
|
||||||
rust_crate_cache::lib *l = cache->get_lib(lib_num, library);
|
rust_crate_cache::lib *l = cache->get_lib(lib_num, library);
|
||||||
return cache->get_c_sym(c_sym_num, l, symbol);
|
return cache->get_c_sym(c_sym_num, l, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL uintptr_t
|
extern "C" CDECL uintptr_t upcall_require_rust_sym(rust_task *task,
|
||||||
upcall_require_rust_sym(rust_task *task,
|
rust_crate const *curr_crate, size_t lib_num, // # of lib
|
||||||
rust_crate const *curr_crate,
|
size_t c_sym_num, // # of C sym "rust_crate" in lib
|
||||||
size_t lib_num, // # of lib
|
size_t rust_sym_num, // # of rust sym
|
||||||
size_t c_sym_num, // # of C sym "rust_crate" in lib
|
char const *library, char const **path) {
|
||||||
size_t rust_sym_num, // # of rust sym
|
|
||||||
char const *library,
|
|
||||||
char const **path)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
rust_dom *dom = task->dom;
|
||||||
|
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"upcall require rust sym: lib #%" PRIdPTR
|
"upcall require rust sym: lib #%" PRIdPTR
|
||||||
" = %s, c_sym #%" PRIdPTR
|
" = %s, c_sym #%" PRIdPTR
|
||||||
", rust_sym #%" PRIdPTR
|
", rust_sym #%" PRIdPTR
|
||||||
", curr_crate = 0x%" PRIxPTR,
|
", curr_crate = 0x%" PRIxPTR, lib_num, library, c_sym_num,
|
||||||
lib_num, library, c_sym_num, rust_sym_num,
|
rust_sym_num, curr_crate);
|
||||||
curr_crate);
|
|
||||||
for (char const **c = crate_rel(curr_crate, path); *c; ++c) {
|
for (char const **c = crate_rel(curr_crate, path); *c; ++c) {
|
||||||
dom->log(rust_log::UPCALL, " + %s", crate_rel(curr_crate, *c));
|
task->log(rust_log::UPCALL, " + %s", crate_rel(curr_crate, *c));
|
||||||
}
|
}
|
||||||
|
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"require C symbol 'rust_crate' from lib #%" PRIdPTR,lib_num);
|
"require C symbol 'rust_crate' from lib #%" PRIdPTR, lib_num);
|
||||||
rust_crate_cache::c_sym *c =
|
rust_crate_cache::c_sym *c =
|
||||||
fetch_c_sym(task, curr_crate, lib_num, c_sym_num,
|
fetch_c_sym(task, curr_crate, lib_num, c_sym_num, library,
|
||||||
library, "rust_crate");
|
"rust_crate");
|
||||||
|
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"require rust symbol inside crate");
|
"require rust symbol inside crate");
|
||||||
rust_crate_cache::rust_sym *s =
|
rust_crate_cache::rust_sym *s = task->cache->get_rust_sym(rust_sym_num,
|
||||||
task->cache->get_rust_sym(rust_sym_num, dom, curr_crate, c, path);
|
dom,
|
||||||
|
curr_crate, c,
|
||||||
|
path);
|
||||||
|
|
||||||
uintptr_t addr = s->get_val();
|
uintptr_t addr = s->get_val();
|
||||||
if (addr) {
|
if (addr) {
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
||||||
} else {
|
} else {
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"failed to resolve symbol");
|
"failed to resolve symbol");
|
||||||
task->fail(7);
|
task->fail(7);
|
||||||
}
|
}
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL uintptr_t
|
extern "C" CDECL uintptr_t upcall_require_c_sym(rust_task *task,
|
||||||
upcall_require_c_sym(rust_task *task,
|
rust_crate const *curr_crate, size_t lib_num, // # of lib
|
||||||
rust_crate const *curr_crate,
|
size_t c_sym_num, // # of C sym
|
||||||
size_t lib_num, // # of lib
|
char const *library, char const *symbol) {
|
||||||
size_t c_sym_num, // # of C sym
|
|
||||||
char const *library,
|
|
||||||
char const *symbol)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
|
||||||
|
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"upcall require c sym: lib #%" PRIdPTR
|
"upcall require c sym: lib #%" PRIdPTR
|
||||||
" = %s, c_sym #%" PRIdPTR
|
" = %s, c_sym #%" PRIdPTR
|
||||||
" = %s"
|
" = %s"
|
||||||
", curr_crate = 0x%" PRIxPTR,
|
", curr_crate = 0x%" PRIxPTR, lib_num, library, c_sym_num,
|
||||||
lib_num, library, c_sym_num, symbol, curr_crate);
|
symbol, curr_crate);
|
||||||
|
|
||||||
rust_crate_cache::c_sym *c =
|
rust_crate_cache::c_sym *c = fetch_c_sym(task, curr_crate, lib_num,
|
||||||
fetch_c_sym(task, curr_crate, lib_num, c_sym_num, library, symbol);
|
c_sym_num, library, symbol);
|
||||||
|
|
||||||
uintptr_t addr = c->get_val();
|
uintptr_t addr = c->get_val();
|
||||||
if (addr) {
|
if (addr) {
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
"found-or-cached addr: 0x%" PRIxPTR, addr);
|
||||||
} else {
|
} else {
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"failed to resolve symbol");
|
"failed to resolve symbol");
|
||||||
task->fail(6);
|
task->fail(6);
|
||||||
}
|
}
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL type_desc *
|
extern "C" CDECL type_desc *
|
||||||
upcall_get_type_desc(rust_task *task,
|
upcall_get_type_desc(rust_task *task, rust_crate const *curr_crate,
|
||||||
rust_crate const *curr_crate,
|
size_t size, size_t align, size_t n_descs, type_desc const **descs) {
|
||||||
size_t size,
|
|
||||||
size_t align,
|
|
||||||
size_t n_descs,
|
|
||||||
type_desc const **descs)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
rust_dom *dom = task->dom;
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
"upcall get_type_desc with size=%" PRIdPTR
|
||||||
"upcall get_type_desc with size=%" PRIdPTR
|
", align=%" PRIdPTR ", %" PRIdPTR " descs", size, align,
|
||||||
", align=%" PRIdPTR ", %" PRIdPTR " descs",
|
n_descs);
|
||||||
size, align, n_descs);
|
|
||||||
rust_crate_cache *cache = task->get_crate_cache(curr_crate);
|
rust_crate_cache *cache = task->get_crate_cache(curr_crate);
|
||||||
type_desc *td = cache->get_type_desc(size, align, n_descs, descs);
|
type_desc *td = cache->get_type_desc(size, align, n_descs, descs);
|
||||||
dom->log(rust_log::UPCALL|rust_log::CACHE,
|
task->log(rust_log::UPCALL | rust_log::CACHE,
|
||||||
"returning tydesc 0x%" PRIxPTR, td);
|
"returning tydesc 0x%" PRIxPTR, td);
|
||||||
return td;
|
return td;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
static DWORD WINAPI rust_thread_start(void *ptr)
|
static DWORD WINAPI rust_thread_start(void *ptr)
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
@ -578,10 +522,10 @@ static void *rust_thread_start(void *ptr)
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// We were handed the domain we are supposed to run.
|
// We were handed the domain we are supposed to run.
|
||||||
rust_dom *dom = (rust_dom *)ptr;
|
rust_dom *dom = (rust_dom *) ptr;
|
||||||
|
|
||||||
// Start a new rust main loop for this thread.
|
// Start a new rust main loop for this thread.
|
||||||
rust_main_loop(dom);
|
dom->start_main_loop();
|
||||||
|
|
||||||
rust_srv *srv = dom->srv;
|
rust_srv *srv = dom->srv;
|
||||||
delete dom;
|
delete dom;
|
||||||
@ -591,81 +535,77 @@ static void *rust_thread_start(void *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_task *
|
extern "C" CDECL rust_task *
|
||||||
upcall_new_task(rust_task *spawner)
|
upcall_new_task(rust_task *spawner) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(spawner);
|
LOG_UPCALL_ENTRY(spawner);
|
||||||
|
|
||||||
rust_dom *dom = spawner->dom;
|
rust_dom *dom = spawner->dom;
|
||||||
rust_task *task = new (dom) rust_task(dom, spawner);
|
rust_task *task = new (dom) rust_task(dom, spawner);
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK,
|
||||||
"upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR,
|
"upcall new_task(spawner 0x%" PRIxPTR ") = 0x%" PRIxPTR,
|
||||||
spawner, task);
|
spawner, task);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_task *
|
extern "C" CDECL rust_task *
|
||||||
upcall_start_task(rust_task *spawner,
|
upcall_start_task(rust_task *spawner, rust_task *task,
|
||||||
rust_task *task,
|
uintptr_t exit_task_glue, uintptr_t spawnee_fn, size_t callsz) {
|
||||||
uintptr_t exit_task_glue,
|
|
||||||
uintptr_t spawnee_fn,
|
|
||||||
size_t callsz)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(spawner);
|
LOG_UPCALL_ENTRY(spawner);
|
||||||
|
|
||||||
rust_dom *dom = spawner->dom;
|
rust_dom *dom = spawner->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK,
|
||||||
"upcall start_task(task 0x%" PRIxPTR
|
"upcall start_task(task 0x%" PRIxPTR
|
||||||
" exit_task_glue 0x%" PRIxPTR
|
" exit_task_glue 0x%" PRIxPTR
|
||||||
", spawnee 0x%" PRIxPTR
|
", spawnee 0x%" PRIxPTR
|
||||||
", callsz %" PRIdPTR ")",
|
", callsz %" PRIdPTR ")", task, exit_task_glue, spawnee_fn,
|
||||||
task, exit_task_glue, spawnee_fn, callsz);
|
callsz);
|
||||||
task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_task *
|
extern "C" CDECL rust_proxy_delegate<rust_task> *
|
||||||
upcall_new_thread(rust_task *task)
|
upcall_new_thread(rust_task *task) {
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(task);
|
LOG_UPCALL_ENTRY(task);
|
||||||
|
|
||||||
rust_dom *old_dom = task->dom;
|
rust_dom *old_dom = task->dom;
|
||||||
rust_dom *new_dom = new rust_dom(old_dom->srv->clone(),
|
rust_dom *new_dom = new rust_dom(old_dom->srv->clone(),
|
||||||
old_dom->root_crate);
|
old_dom->root_crate);
|
||||||
new_dom->log(rust_log::UPCALL|rust_log::MEM,
|
|
||||||
"upcall new_thread() = 0x%" PRIxPTR,
|
task->log(rust_log::UPCALL | rust_log::MEM,
|
||||||
new_dom->root_task);
|
"upcall new_thread() = dom 0x%" PRIxPTR " task 0x%" PRIxPTR,
|
||||||
return new_dom->root_task;
|
new_dom, new_dom->root_task);
|
||||||
|
rust_proxy<rust_task> *proxy =
|
||||||
|
new (old_dom) rust_proxy<rust_task>(old_dom, new_dom->root_task);
|
||||||
|
task->log(rust_log::UPCALL | rust_log::MEM,
|
||||||
|
"new proxy = 0x%" PRIxPTR " -> task = 0x%" PRIxPTR,
|
||||||
|
proxy, proxy->delegate());
|
||||||
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_task *
|
extern "C" CDECL rust_proxy_delegate<rust_task> *
|
||||||
upcall_start_thread(rust_task *spawner,
|
upcall_start_thread(rust_task *spawner,
|
||||||
rust_task *root_task,
|
rust_proxy_delegate<rust_task> *root_task_proxy,
|
||||||
uintptr_t exit_task_glue,
|
uintptr_t exit_task_glue, uintptr_t spawnee_fn, size_t callsz) {
|
||||||
uintptr_t spawnee_fn,
|
|
||||||
size_t callsz)
|
|
||||||
{
|
|
||||||
LOG_UPCALL_ENTRY(spawner);
|
LOG_UPCALL_ENTRY(spawner);
|
||||||
|
|
||||||
rust_dom *dom = spawner->dom;
|
rust_dom *dom = spawner->dom;
|
||||||
dom->log(rust_log::UPCALL|rust_log::MEM|rust_log::TASK,
|
rust_task *root_task = root_task_proxy->delegate();
|
||||||
|
dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK,
|
||||||
"upcall start_thread(exit_task_glue 0x%" PRIxPTR
|
"upcall start_thread(exit_task_glue 0x%" PRIxPTR
|
||||||
", spawnee 0x%" PRIxPTR
|
", spawnee 0x%" PRIxPTR
|
||||||
", callsz %" PRIdPTR ")",
|
", callsz %" PRIdPTR ")", exit_task_glue, spawnee_fn, callsz);
|
||||||
exit_task_glue, spawnee_fn, callsz);
|
|
||||||
root_task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
root_task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz);
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
thread = CreateThread(NULL, 0, rust_thread_start, root_task->dom,
|
thread = CreateThread(NULL, 0, rust_thread_start, root_task->dom,
|
||||||
0, NULL);
|
0, NULL);
|
||||||
dom->win32_require("CreateThread", thread != NULL);
|
dom->win32_require("CreateThread", thread != NULL);
|
||||||
#else
|
#else
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_create(&thread, &dom->attr, rust_thread_start,
|
pthread_create(&thread, &dom->attr, rust_thread_start,
|
||||||
(void *)root_task->dom);
|
(void *) root_task->dom);
|
||||||
#endif
|
#endif
|
||||||
|
return root_task_proxy;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
rc_base<T>::rc_base() :
|
rc_base<T>::rc_base() :
|
||||||
refcnt(1)
|
ref_count(1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ ptr_vec<T>::trim(size_t sz)
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void
|
void
|
||||||
ptr_vec<T>::swapdel(T *item)
|
ptr_vec<T>::swap_delete(T *item)
|
||||||
{
|
{
|
||||||
/* Swap the endpoint into i and decr fill. */
|
/* Swap the endpoint into i and decr fill. */
|
||||||
I(dom, data);
|
I(dom, data);
|
||||||
|
66
src/rt/sync/condition_variable.cpp
Normal file
66
src/rt/sync/condition_variable.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include "../globals.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conditional variable. Implemented using pthreads condition variables, and
|
||||||
|
* using events on windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "condition_variable.h"
|
||||||
|
|
||||||
|
// #define TRACE
|
||||||
|
|
||||||
|
condition_variable::condition_variable() {
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
#else
|
||||||
|
pthread_cond_init(&_cond, NULL);
|
||||||
|
pthread_mutex_init(&_mutex, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
condition_variable::~condition_variable() {
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
CloseHandle(_event);
|
||||||
|
#else
|
||||||
|
pthread_cond_destroy(&_cond);
|
||||||
|
pthread_mutex_destroy(&_mutex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait indefinitely until condition is signaled.
|
||||||
|
*/
|
||||||
|
void condition_variable::wait() {
|
||||||
|
#ifdef TRACE
|
||||||
|
printf("waiting on condition_variable: 0x%" PRIxPTR "\n",
|
||||||
|
(uintptr_t)this);
|
||||||
|
#endif
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
WaitForSingleObject(_event, INFINITE);
|
||||||
|
#else
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
pthread_cond_wait(&_cond, &_mutex);
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
#endif
|
||||||
|
#ifdef TRACE
|
||||||
|
printf("resumed on condition_variable: 0x%" PRIxPTR "\n",
|
||||||
|
(uintptr_t)this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal condition, and resume the waiting thread.
|
||||||
|
*/
|
||||||
|
void condition_variable::signal() {
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
SetEvent(_event);
|
||||||
|
#else
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
pthread_cond_signal(&_cond);
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
#endif
|
||||||
|
#ifdef TRACE
|
||||||
|
printf("signal condition_variable: 0x%" PRIxPTR "\n",
|
||||||
|
(uintptr_t)this);
|
||||||
|
#endif
|
||||||
|
}
|
19
src/rt/sync/condition_variable.h
Normal file
19
src/rt/sync/condition_variable.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef CONDITION_VARIABLE_H
|
||||||
|
#define CONDITION_VARIABLE_H
|
||||||
|
|
||||||
|
class condition_variable {
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
HANDLE _event;
|
||||||
|
#else
|
||||||
|
pthread_cond_t _cond;
|
||||||
|
pthread_mutex_t _mutex;
|
||||||
|
#endif
|
||||||
|
public:
|
||||||
|
condition_variable();
|
||||||
|
virtual ~condition_variable();
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
void signal();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* CONDITION_VARIABLE_H */
|
@ -5,33 +5,46 @@
|
|||||||
* dequeue() is not allowed to interrupt itself.
|
* dequeue() is not allowed to interrupt itself.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "../globals.h"
|
||||||
#include "lock_free_queue.h"
|
#include "lock_free_queue.h"
|
||||||
|
|
||||||
lock_free_queue::lock_free_queue() :
|
lock_free_queue_node::lock_free_queue_node() : next(NULL) {
|
||||||
tail(this) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock_free_queue::enqueue(lock_free_queue_node *item) {
|
lock_free_queue::lock_free_queue() : _tail(this) {
|
||||||
item->next = (lock_free_queue_node *) 0;
|
|
||||||
lock_free_queue_node *last = tail;
|
}
|
||||||
tail = item;
|
|
||||||
while (last->next)
|
void
|
||||||
|
lock_free_queue::enqueue(lock_free_queue_node *item) {
|
||||||
|
item->next = (lock_free_queue_node *) NULL;
|
||||||
|
lock_free_queue_node *last = _tail;
|
||||||
|
_tail = item;
|
||||||
|
while (last->next) {
|
||||||
last = last->next;
|
last = last->next;
|
||||||
|
}
|
||||||
last->next = item;
|
last->next = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock_free_queue_node *lockfree_queue::dequeue() {
|
lock_free_queue_node *
|
||||||
|
lock_free_queue::dequeue() {
|
||||||
lock_free_queue_node *item = next;
|
lock_free_queue_node *item = next;
|
||||||
if (item && !(next = item->next)) {
|
if (item && !(next = item->next)) {
|
||||||
tail = (lock_free_queue_node *) this;
|
_tail = (lock_free_queue_node *) this;
|
||||||
if (item->next) {
|
if (item->next) {
|
||||||
lock_free_queue_node *lost = item->next;
|
lock_free_queue_node *lost = item->next;
|
||||||
lock_free_queue_node *help;
|
lock_free_queue_node *help;
|
||||||
do {
|
do {
|
||||||
help = lost->next;
|
help = lost->next;
|
||||||
enqueue(lost);
|
enqueue(lost);
|
||||||
} while ((lost = help) != (lock_free_queue_node *) 0);
|
} while ((lost = help) != (lock_free_queue_node *) NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lock_free_queue::is_empty() {
|
||||||
|
return next == NULL;
|
||||||
|
}
|
||||||
|
@ -2,14 +2,18 @@
|
|||||||
#define LOCK_FREE_QUEUE_H
|
#define LOCK_FREE_QUEUE_H
|
||||||
|
|
||||||
class lock_free_queue_node {
|
class lock_free_queue_node {
|
||||||
|
public:
|
||||||
lock_free_queue_node *next;
|
lock_free_queue_node *next;
|
||||||
|
lock_free_queue_node();
|
||||||
};
|
};
|
||||||
|
|
||||||
class lock_free_queue {
|
class lock_free_queue : lock_free_queue_node {
|
||||||
|
lock_free_queue_node *_tail;
|
||||||
public:
|
public:
|
||||||
lock_free_queue();
|
lock_free_queue();
|
||||||
void enqueue(lock_free_queue_node *item);
|
void enqueue(lock_free_queue_node *item);
|
||||||
lock_free_queue_node *dequeue();
|
lock_free_queue_node *dequeue();
|
||||||
|
bool is_empty();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LOCK_FREE_QUEUE_H */
|
#endif /* LOCK_FREE_QUEUE_H */
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
#include "../globals.h"
|
||||||
|
#include "spin_lock.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Your average spin lock.
|
* Your average spin lock.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
// #define TRACE
|
// #define TRACE
|
||||||
|
|
||||||
spin_lock::spin_lock() {
|
spin_lock::spin_lock() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#ifndef UNFAIR_TICKET_LOCK_H
|
#ifndef SPIN_LOCK_H
|
||||||
#define UNFAIR_TICKET_LOCK_H
|
#define SPIN_LOCK_H
|
||||||
|
|
||||||
class spin_lock {
|
class spin_lock {
|
||||||
unsigned ticket;
|
unsigned ticket;
|
||||||
@ -11,4 +11,4 @@ public:
|
|||||||
void unlock();
|
void unlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* UNFAIR_TICKET_LOCK_H */
|
#endif /* SPIN_LOCK_H */
|
||||||
|
@ -13,38 +13,44 @@ public:
|
|||||||
array_list();
|
array_list();
|
||||||
~array_list();
|
~array_list();
|
||||||
size_t size();
|
size_t size();
|
||||||
void append(T value);
|
int32_t append(T value);
|
||||||
T replace(T old_value, T new_value);
|
T replace(T old_value, T new_value);
|
||||||
size_t index_of(T value);
|
int32_t index_of(T value);
|
||||||
T & operator[](size_t index);
|
T & operator[](size_t index);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> array_list<T>::array_list() {
|
template<typename T>
|
||||||
|
array_list<T>::array_list() {
|
||||||
_capacity = INITIAL_CAPACITY;
|
_capacity = INITIAL_CAPACITY;
|
||||||
_data = (T *) malloc(sizeof(T) * _capacity);
|
_data = (T *) malloc(sizeof(T) * _capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> array_list<T>::~array_list() {
|
template<typename T>
|
||||||
|
array_list<T>::~array_list() {
|
||||||
delete _data;
|
delete _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> size_t array_list<T>::size() {
|
template<typename T> size_t
|
||||||
|
array_list<T>::size() {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> void array_list<T>::append(T value) {
|
template<typename T> int32_t
|
||||||
|
array_list<T>::append(T value) {
|
||||||
if (_size == _capacity) {
|
if (_size == _capacity) {
|
||||||
_capacity = _capacity * 2;
|
_capacity = _capacity * 2;
|
||||||
_data = (T *) realloc(_data, _capacity * sizeof(T));
|
_data = (T *) realloc(_data, _capacity * sizeof(T));
|
||||||
}
|
}
|
||||||
_data[_size++] = value;
|
_data[_size ++] = value;
|
||||||
|
return _size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the old_value in the list with the new_value.
|
* Replaces the old_value in the list with the new_value.
|
||||||
* Returns the old_value if the replacement succeeded, or NULL otherwise.
|
* Returns the old_value if the replacement succeeded, or NULL otherwise.
|
||||||
*/
|
*/
|
||||||
template<typename T> T array_list<T>::replace(T old_value, T new_value) {
|
template<typename T> T
|
||||||
|
array_list<T>::replace(T old_value, T new_value) {
|
||||||
int index = index_of(old_value);
|
int index = index_of(old_value);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -53,7 +59,8 @@ template<typename T> T array_list<T>::replace(T old_value, T new_value) {
|
|||||||
return old_value;
|
return old_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> size_t array_list<T>::index_of(T value) {
|
template<typename T> int32_t
|
||||||
|
array_list<T>::index_of(T value) {
|
||||||
for (size_t i = 0; i < _size; i++) {
|
for (size_t i = 0; i < _size; i++) {
|
||||||
if (_data[i] == value) {
|
if (_data[i] == value) {
|
||||||
return i;
|
return i;
|
||||||
@ -62,7 +69,8 @@ template<typename T> size_t array_list<T>::index_of(T value) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> T & array_list<T>::operator[](size_t index) {
|
template<typename T> T &
|
||||||
|
array_list<T>::operator[](size_t index) {
|
||||||
return _data[index];
|
return _data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
src/test/run-pass/task-comm-0.rs
Normal file
19
src/test/run-pass/task-comm-0.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
io fn main() -> () {
|
||||||
|
test05();
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test05_start(chan[int] ch) {
|
||||||
|
ch <| 10;
|
||||||
|
ch <| 20;
|
||||||
|
ch <| 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test05() {
|
||||||
|
let port[int] po = port();
|
||||||
|
let chan[int] ch = chan(po);
|
||||||
|
spawn test05_start(chan(po));
|
||||||
|
let int value <- po;
|
||||||
|
value <- po;
|
||||||
|
value <- po;
|
||||||
|
log value;
|
||||||
|
}
|
13
src/test/run-pass/task-comm-1.rs
Normal file
13
src/test/run-pass/task-comm-1.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
fn main() -> () {
|
||||||
|
test00();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start() {
|
||||||
|
log "Started / Finished Task.";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test00() {
|
||||||
|
let task t = spawn thread start();
|
||||||
|
join t;
|
||||||
|
log "Completing.";
|
||||||
|
}
|
34
src/test/run-pass/task-comm-2.rs
Normal file
34
src/test/run-pass/task-comm-2.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
fn main() -> () {
|
||||||
|
log "===== THREADS =====";
|
||||||
|
test00(true);
|
||||||
|
log "====== TASKS ======";
|
||||||
|
test00(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(int task_number) {
|
||||||
|
log "Started task.";
|
||||||
|
let int i = 0;
|
||||||
|
while (i < 10000) {
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
log "Finished task.";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test00(bool create_threads) {
|
||||||
|
let int number_of_tasks = 32;
|
||||||
|
|
||||||
|
let int i = 0;
|
||||||
|
let vec[task] tasks = vec();
|
||||||
|
while (i < number_of_tasks) {
|
||||||
|
i = i + 1;
|
||||||
|
if (create_threads) {
|
||||||
|
tasks += vec(spawn thread start(i));
|
||||||
|
} else {
|
||||||
|
tasks += vec(spawn start(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (task t in tasks) {
|
||||||
|
join t;
|
||||||
|
}
|
||||||
|
}
|
59
src/test/run-pass/task-comm-3.rs
Normal file
59
src/test/run-pass/task-comm-3.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
io fn main() -> () {
|
||||||
|
log "===== THREADS =====";
|
||||||
|
test00(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test00_start(chan[int] ch, int message, int count) {
|
||||||
|
log "Starting test00_start";
|
||||||
|
let int i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
log "Sending Message";
|
||||||
|
ch <| message;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
log "Ending test00_start";
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test00(bool is_multithreaded) {
|
||||||
|
let int number_of_tasks = 1;
|
||||||
|
let int number_of_messages = 0;
|
||||||
|
log "Creating tasks";
|
||||||
|
|
||||||
|
let port[int] po = port();
|
||||||
|
let chan[int] ch = chan(po);
|
||||||
|
|
||||||
|
let int i = 0;
|
||||||
|
|
||||||
|
// Create and spawn tasks...
|
||||||
|
let vec[task] tasks = vec();
|
||||||
|
while (i < number_of_tasks) {
|
||||||
|
i = i + 1;
|
||||||
|
if (is_multithreaded) {
|
||||||
|
tasks += vec(
|
||||||
|
spawn thread test00_start(ch, i, number_of_messages));
|
||||||
|
} else {
|
||||||
|
tasks += vec(spawn test00_start(ch, i, number_of_messages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from spawned tasks...
|
||||||
|
let int sum = 0;
|
||||||
|
for (task t in tasks) {
|
||||||
|
i = 0;
|
||||||
|
while (i < number_of_messages) {
|
||||||
|
let int value <- po;
|
||||||
|
sum += value;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join spawned tasks...
|
||||||
|
for (task t in tasks) {
|
||||||
|
join t;
|
||||||
|
}
|
||||||
|
|
||||||
|
log "Completed: Final number is: ";
|
||||||
|
check (sum + 1 == number_of_messages *
|
||||||
|
(number_of_tasks * number_of_tasks + number_of_tasks) / 2);
|
||||||
|
log sum;
|
||||||
|
}
|
10
src/test/run-pass/task-comm-4.rs
Normal file
10
src/test/run-pass/task-comm-4.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
io fn main() -> () {
|
||||||
|
test00();
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test00() {
|
||||||
|
let port[int] p = port();
|
||||||
|
let chan[int] c = chan(p);
|
||||||
|
c <| 42;
|
||||||
|
let int r <- p;
|
||||||
|
}
|
@ -1,17 +1,19 @@
|
|||||||
|
|
||||||
|
fn main() -> () {
|
||||||
io fn main() -> () {
|
// test00(true);
|
||||||
test00(true);
|
|
||||||
// test01();
|
// test01();
|
||||||
// test02();
|
// test02();
|
||||||
// test03();
|
// test03();
|
||||||
// test04();
|
// test04();
|
||||||
|
// test05();
|
||||||
|
test06();
|
||||||
}
|
}
|
||||||
|
|
||||||
io fn test00_start(chan[int] ch, int message, int count) {
|
io fn test00_start(chan[int] ch, int message, int count) {
|
||||||
log "Starting test00_start";
|
log "Starting test00_start";
|
||||||
let int i = 0;
|
let int i = 0;
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
|
log "Sending Message";
|
||||||
ch <| message;
|
ch <| message;
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
@ -19,7 +21,7 @@ io fn test00_start(chan[int] ch, int message, int count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
io fn test00(bool is_multithreaded) {
|
io fn test00(bool is_multithreaded) {
|
||||||
let int number_of_tasks = 4;
|
let int number_of_tasks = 1;
|
||||||
let int number_of_messages = 64;
|
let int number_of_messages = 64;
|
||||||
log "Creating tasks";
|
log "Creating tasks";
|
||||||
|
|
||||||
@ -109,12 +111,50 @@ fn test04() {
|
|||||||
log "Finishing up.";
|
log "Finishing up.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io fn test05_start(chan[int] ch) {
|
||||||
|
ch <| 10;
|
||||||
|
ch <| 20;
|
||||||
|
ch <| 30;
|
||||||
|
ch <| 30;
|
||||||
|
ch <| 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
io fn test05() {
|
||||||
|
let port[int] po = port();
|
||||||
|
let chan[int] ch = chan(po);
|
||||||
|
spawn thread test05_start(ch);
|
||||||
|
let int value <- po;
|
||||||
|
value <- po;
|
||||||
|
value <- po;
|
||||||
|
log value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test06_start(int task_number) {
|
||||||
|
log "Started task.";
|
||||||
|
let int i = 0;
|
||||||
|
while (i < 100000000) {
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
log "Finished task.";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test06() {
|
||||||
|
let int number_of_tasks = 32;
|
||||||
|
log "Creating tasks";
|
||||||
|
|
||||||
|
let int i = 0;
|
||||||
|
|
||||||
|
let vec[task] tasks = vec();
|
||||||
|
while (i < number_of_tasks) {
|
||||||
|
i = i + 1;
|
||||||
|
tasks += vec(spawn thread test06_start(i));
|
||||||
|
// tasks += vec(spawn test06_start(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (task t in tasks) {
|
||||||
|
join t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user