diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2012-02-01 18:50:19 -0800 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2012-02-01 18:50:19 -0800 |
| commit | 31166438063236e6d2ad021cf2169fce2b3839b4 (patch) | |
| tree | c42edd27f47c8a78cc212391c093992d3bde7f4b /src/rt | |
| parent | c36207bfb82f794f17fc5854d4ae50284eddf329 (diff) | |
| download | rust-31166438063236e6d2ad021cf2169fce2b3839b4.tar.gz rust-31166438063236e6d2ad021cf2169fce2b3839b4.zip | |
Revert "make boxes self-describing (fixes #1493)" until a new
snapshot is prepared.
Diffstat (limited to 'src/rt')
| -rw-r--r-- | src/rt/boxed_region.cpp | 59 | ||||
| -rw-r--r-- | src/rt/boxed_region.h | 39 | ||||
| -rw-r--r-- | src/rt/memory_region.h | 8 | ||||
| -rw-r--r-- | src/rt/rust_builtin.cpp | 16 | ||||
| -rw-r--r-- | src/rt/rust_cc.cpp | 202 | ||||
| -rw-r--r-- | src/rt/rust_internal.h | 31 | ||||
| -rw-r--r-- | src/rt/rust_shape.cpp | 2 | ||||
| -rw-r--r-- | src/rt/rust_shape.h | 104 | ||||
| -rw-r--r-- | src/rt/rust_task.cpp | 47 | ||||
| -rw-r--r-- | src/rt/rust_task.h | 10 | ||||
| -rw-r--r-- | src/rt/rust_upcall.cpp | 60 | ||||
| -rw-r--r-- | src/rt/rust_upcall.h | 16 | ||||
| -rw-r--r-- | src/rt/rustrt.def.in | 3 | ||||
| -rw-r--r-- | src/rt/test/rust_test_runtime.cpp | 2 |
14 files changed, 313 insertions, 286 deletions
diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp deleted file mode 100644 index 937069bc38c..00000000000 --- a/src/rt/boxed_region.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include <assert.h> -#include "boxed_region.h" -#include "rust_internal.h" - -// #define DUMP_BOXED_REGION - -rust_opaque_box *boxed_region::malloc(type_desc *td) { - size_t header_size = sizeof(rust_opaque_box); - size_t body_size = td->size; - size_t body_align = td->align; - size_t total_size = align_to(header_size, body_align) + body_size; - rust_opaque_box *box = - (rust_opaque_box*)backing_region->malloc(total_size, "@"); - box->td = td; - box->ref_count = 1; - box->prev = NULL; - box->next = live_allocs; - if (live_allocs) live_allocs->prev = box; - live_allocs = box; - -# ifdef DUMP_BOXED_REGION - fprintf(stderr, "Allocated box %p with td %p," - " size %lu==%lu+%lu, align %lu, prev %p, next %p\n", - box, td, total_size, header_size, body_size, body_align, - box->prev, box->next); -# endif - - return box; -} - -rust_opaque_box *boxed_region::calloc(type_desc *td) { - rust_opaque_box *box = malloc(td); - memset(box_body(box), 0, td->size); - return box; -} - -void boxed_region::free(rust_opaque_box *box) { - // This turns out to not be true in various situations, - // like when we are unwinding after a failure. - // - // assert(box->ref_count == 0); - - // This however should always be true. Helps to detect - // double frees (kind of). - assert(box->td != NULL); - -# ifdef DUMP_BOXED_REGION - fprintf(stderr, "Freed box %p with td %p, prev %p, next %p\n", - box, box->td, box->prev, box->next); -# endif - - if (box->prev) box->prev->next = box->next; - if (box->next) box->next->prev = box->prev; - if (live_allocs == box) live_allocs = box->next; - box->prev = NULL; - box->next = NULL; - box->td = NULL; - backing_region->free(box); -} diff --git a/src/rt/boxed_region.h b/src/rt/boxed_region.h deleted file mode 100644 index bd5312fee99..00000000000 --- a/src/rt/boxed_region.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef BOXED_REGION_H -#define BOXED_REGION_H - -#include <stdlib.h> - -struct type_desc; -class memory_region; -struct rust_opaque_box; - -/* Tracks the data allocated by a particular task in the '@' region. - * Currently still relies on the standard malloc as a backing allocator, but - * this could be improved someday if necessary. Every allocation must provide - * a type descr which describes the payload (what follows the header). */ -class boxed_region { -private: - memory_region *backing_region; - rust_opaque_box *live_allocs; - - size_t align_to(size_t v, size_t align) { - size_t alignm1 = align - 1; - v += alignm1; - v &= ~alignm1; - return v; - } - -public: - boxed_region(memory_region *br) - : backing_region(br) - , live_allocs(NULL) - {} - - rust_opaque_box *first_live_alloc() { return live_allocs; } - - rust_opaque_box *malloc(type_desc *td); - rust_opaque_box *calloc(type_desc *td); - void free(rust_opaque_box *box); -}; - -#endif /* BOXED_REGION_H */ diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h index a3d7270e79d..1a3e94c10bd 100644 --- a/src/rt/memory_region.h +++ b/src/rt/memory_region.h @@ -50,9 +50,6 @@ private: void dec_alloc(); void maybe_poison(void *mem); - void release_alloc(void *mem); - void claim_alloc(void *mem); - public: memory_region(rust_srv *srv, bool synchronized); memory_region(memory_region *parent); @@ -61,7 +58,10 @@ public: void *realloc(void *mem, size_t size); void free(void *mem); virtual ~memory_region(); - }; + + void release_alloc(void *mem); + void claim_alloc(void *mem); +}; inline void *operator new(size_t size, memory_region ®ion, const char *tag) { diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index ea2e8de256c..89d8c6d12f2 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -429,6 +429,22 @@ start_task(rust_task_id id, fn_env_pair *f) { target->deref(); } +extern "C" CDECL void +migrate_alloc(void *alloc, rust_task_id tid) { + rust_task *task = rust_scheduler::get_task(); + if(!alloc) return; + rust_task *target = task->kernel->get_task_by_id(tid); + if(target) { + const type_desc *tydesc = task->release_alloc(alloc); + target->claim_alloc(alloc, tydesc); + target->deref(); + } + else { + // We couldn't find the target. Maybe we should just free? + task->fail(); + } +} + extern "C" CDECL int sched_threads() { rust_task *task = rust_scheduler::get_task(); diff --git a/src/rt/rust_cc.cpp b/src/rt/rust_cc.cpp index 60b0b8d2fd0..78138434d73 100644 --- a/src/rt/rust_cc.cpp +++ b/src/rt/rust_cc.cpp @@ -25,7 +25,7 @@ namespace cc { // Internal reference count computation -typedef std::map<rust_opaque_box*,uintptr_t> irc_map; +typedef std::map<void *,uintptr_t> irc_map; class irc : public shape::data<irc,shape::ptr> { friend class shape::data<irc,shape::ptr>; @@ -118,6 +118,13 @@ class irc : public shape::data<irc,shape::ptr> { } } + void walk_obj2() { + dp += sizeof(void *); // skip vtable + uint8_t *box_ptr = shape::bump_dp<uint8_t *>(dp); + shape::ptr ref_count_dp(box_ptr); + maybe_record_irc(ref_count_dp); + } + void walk_iface2() { walk_box2(); } @@ -138,32 +145,30 @@ class irc : public shape::data<irc,shape::ptr> { void walk_uniq_contents2(irc &sub) { sub.walk(); } - void walk_box_contents2(irc &sub, shape::ptr &box_dp) { - maybe_record_irc(box_dp); + void walk_box_contents2(irc &sub, shape::ptr &ref_count_dp) { + maybe_record_irc(ref_count_dp); // Do not traverse the contents of this box; it's in the allocation // somewhere, so we're guaranteed to come back to it (if we haven't // traversed it already). } - void maybe_record_irc(shape::ptr &box_dp) { - if (!box_dp) + void maybe_record_irc(shape::ptr &ref_count_dp) { + if (!ref_count_dp) return; - rust_opaque_box *box_ptr = (rust_opaque_box *) box_dp; - // Bump the internal reference count of the box. - if (ircs.find(box_ptr) == ircs.end()) { + if (ircs.find((void *)ref_count_dp) == ircs.end()) { LOG(task, gc, "setting internal reference count for %p to 1", - box_ptr); - ircs[box_ptr] = 1; + (void *)ref_count_dp); + ircs[(void *)ref_count_dp] = 1; } else { - uintptr_t newcount = ircs[box_ptr] + 1; + uintptr_t newcount = ircs[(void *)ref_count_dp] + 1; LOG(task, gc, "bumping internal reference count for %p to %lu", - box_ptr, newcount); - ircs[box_ptr] = newcount; + (void *)ref_count_dp, newcount); + ircs[(void *)ref_count_dp] = newcount; } } @@ -202,25 +207,36 @@ irc::walk_variant2(shape::tag_info &tinfo, uint32_t variant_id, void irc::compute_ircs(rust_task *task, irc_map &ircs) { - boxed_region *boxed = &task->boxed; - for (rust_opaque_box *box = boxed->first_live_alloc(); - box != NULL; - box = box->next) { - type_desc *tydesc = box->td; - uint8_t *body = (uint8_t*) box_body(box); - - LOG(task, gc, - "determining internal ref counts: " - "box=%p tydesc=%p body=%p", - box, tydesc, body); - + std::map<void *,const type_desc *>::iterator + begin(task->local_allocs.begin()), end(task->local_allocs.end()); + while (begin != end) { + uint8_t *p = reinterpret_cast<uint8_t *>(begin->first); + + const type_desc *tydesc = begin->second; + + LOG(task, gc, "determining internal ref counts: %p, tydesc=%p", p, + tydesc); + shape::arena arena; shape::type_param *params = - shape::type_param::from_tydesc_and_data(tydesc, body, arena); + shape::type_param::from_tydesc_and_data(tydesc, p, arena); + +#if 0 + shape::print print(task, true, tydesc->shape, params, + tydesc->shape_tables); + print.walk(); + + shape::log log(task, true, tydesc->shape, params, + tydesc->shape_tables, p + sizeof(uintptr_t), + std::cerr); + log.walk(); +#endif irc irc(task, true, tydesc->shape, params, tydesc->shape_tables, - body, ircs); + p + sizeof(uintptr_t), ircs); irc.walk(); + + ++begin; } } @@ -228,17 +244,17 @@ irc::compute_ircs(rust_task *task, irc_map &ircs) { // Root finding void -find_roots(rust_task *task, irc_map &ircs, - std::vector<rust_opaque_box *> &roots) { - boxed_region *boxed = &task->boxed; - for (rust_opaque_box *box = boxed->first_live_alloc(); - box != NULL; - box = box->next) { - uintptr_t ref_count = box->ref_count; +find_roots(rust_task *task, irc_map &ircs, std::vector<void *> &roots) { + std::map<void *,const type_desc *>::iterator + begin(task->local_allocs.begin()), end(task->local_allocs.end()); + while (begin != end) { + void *alloc = begin->first; + uintptr_t *ref_count_ptr = reinterpret_cast<uintptr_t *>(alloc); + uintptr_t ref_count = *ref_count_ptr; uintptr_t irc; - if (ircs.find(box) != ircs.end()) - irc = ircs[box]; + if (ircs.find(alloc) != ircs.end()) + irc = ircs[alloc]; else irc = 0; @@ -246,14 +262,16 @@ find_roots(rust_task *task, irc_map &ircs, // This allocation must be a root, because the internal reference // count is smaller than the total reference count. LOG(task, gc,"root found: %p, irc %lu, ref count %lu", - box, irc, ref_count); - roots.push_back(box); + alloc, irc, ref_count); + roots.push_back(alloc); } else { LOG(task, gc, "nonroot found: %p, irc %lu, ref count %lu", - box, irc, ref_count); + alloc, irc, ref_count); assert(irc == ref_count && "Internal reference count must be " "less than or equal to the total reference count!"); } + + ++begin; } } @@ -263,7 +281,7 @@ find_roots(rust_task *task, irc_map &ircs, class mark : public shape::data<mark,shape::ptr> { friend class shape::data<mark,shape::ptr>; - std::set<rust_opaque_box *> &marked; + std::set<void *> &marked; mark(const mark &other, const shape::ptr &in_dp) : shape::data<mark,shape::ptr>(other.task, other.align, other.sp, @@ -301,7 +319,7 @@ class mark : public shape::data<mark,shape::ptr> { const shape::type_param *in_params, const rust_shape_tables *in_tables, uint8_t *in_data, - std::set<rust_opaque_box*> &in_marked) + std::set<void *> &in_marked) : shape::data<mark,shape::ptr>(in_task, in_align, in_sp, in_params, in_tables, in_data), marked(in_marked) {} @@ -339,7 +357,7 @@ class mark : public shape::data<mark,shape::ptr> { case shape::SHAPE_BOX_FN: { // Record an irc for the environment box, but don't descend // into it since it will be walked via the box's allocation - shape::data<mark,shape::ptr>::walk_fn_contents1(); + shape::data<mark,shape::ptr>::walk_fn_contents1(dp, false); break; } case shape::SHAPE_BARE_FN: // Does not close over data. @@ -350,6 +368,10 @@ class mark : public shape::data<mark,shape::ptr> { } } + void walk_obj2() { + shape::data<mark,shape::ptr>::walk_obj_contents1(dp); + } + void walk_res2(const shape::rust_fn *dtor, unsigned n_params, const shape::type_param *params, const uint8_t *end_sp, bool live) { @@ -370,16 +392,14 @@ class mark : public shape::data<mark,shape::ptr> { void walk_uniq_contents2(mark &sub) { sub.walk(); } - void walk_box_contents2(mark &sub, shape::ptr &box_dp) { - if (!box_dp) + void walk_box_contents2(mark &sub, shape::ptr &ref_count_dp) { + if (!ref_count_dp) return; - rust_opaque_box *box_ptr = (rust_opaque_box *) box_dp; - - if (marked.find(box_ptr) != marked.end()) + if (marked.find((void *)ref_count_dp) != marked.end()) return; // Skip to avoid chasing cycles. - marked.insert(box_ptr); + marked.insert((void *)ref_count_dp); sub.walk(); } @@ -398,9 +418,8 @@ class mark : public shape::data<mark,shape::ptr> { inline void walk_number2() { /* no-op */ } public: - static void do_mark(rust_task *task, - const std::vector<rust_opaque_box *> &roots, - std::set<rust_opaque_box*> &marked); + static void do_mark(rust_task *task, const std::vector<void *> &roots, + std::set<void *> &marked); }; void @@ -419,28 +438,35 @@ mark::walk_variant2(shape::tag_info &tinfo, uint32_t variant_id, } void -mark::do_mark(rust_task *task, - const std::vector<rust_opaque_box *> &roots, - std::set<rust_opaque_box *> &marked) { - std::vector<rust_opaque_box *>::const_iterator - begin(roots.begin()), - end(roots.end()); +mark::do_mark(rust_task *task, const std::vector<void *> &roots, + std::set<void *> &marked) { + std::vector<void *>::const_iterator begin(roots.begin()), + end(roots.end()); while (begin != end) { - rust_opaque_box *box = *begin; - if (marked.find(box) == marked.end()) { - marked.insert(box); + void *alloc = *begin; + if (marked.find(alloc) == marked.end()) { + marked.insert(alloc); - const type_desc *tydesc = box->td; + const type_desc *tydesc = task->local_allocs[alloc]; - LOG(task, gc, "marking: %p, tydesc=%p", box, tydesc); + LOG(task, gc, "marking: %p, tydesc=%p", alloc, tydesc); - uint8_t *p = (uint8_t*) box_body(box); + uint8_t *p = reinterpret_cast<uint8_t *>(alloc); shape::arena arena; shape::type_param *params = shape::type_param::from_tydesc_and_data(tydesc, p, arena); +#if 0 + // We skip over the reference count here. + shape::log log(task, true, tydesc->shape, params, + tydesc->shape_tables, p + sizeof(uintptr_t), + std::cerr); + log.walk(); +#endif + + // We skip over the reference count here. mark mark(task, true, tydesc->shape, params, tydesc->shape_tables, - p, marked); + p + sizeof(uintptr_t), marked); mark.walk(); } @@ -526,9 +552,13 @@ class sweep : public shape::data<sweep,shape::ptr> { fn_env_pair pair = *(fn_env_pair*)dp; // free closed over data: - shape::data<sweep,shape::ptr>::walk_fn_contents1(); + shape::data<sweep,shape::ptr>::walk_fn_contents1(dp, true); // now free the embedded type descr: + // + // see comment in walk_fn_contents1() concerning null_td + // to understand why this does not occur during the normal + // walk. upcall_s_free_shared_type_desc((type_desc*)pair.env->td); // now free the ptr: @@ -580,7 +610,7 @@ class sweep : public shape::data<sweep,shape::ptr> { void walk_uniq_contents2(sweep &sub) { sub.walk(); } - void walk_box_contents2(sweep &sub, shape::ptr &box_dp) { + void walk_box_contents2(sweep &sub, shape::ptr &ref_count_dp) { return; } @@ -607,50 +637,50 @@ class sweep : public shape::data<sweep,shape::ptr> { inline void walk_number2() { /* no-op */ } public: - static void do_sweep(rust_task *task, - const std::set<rust_opaque_box*> &marked); + static void do_sweep(rust_task *task, const std::set<void *> &marked); }; void -sweep::do_sweep(rust_task *task, - const std::set<rust_opaque_box*> &marked) { - boxed_region *boxed = &task->boxed; - rust_opaque_box *box = boxed->first_live_alloc(); - while (box != NULL) { - // save next ptr as we may be freeing box - rust_opaque_box *box_next = box->next; - if (marked.find(box) == marked.end()) { - LOG(task, gc, "object is part of a cycle: %p", box); - - const type_desc *tydesc = box->td; - uint8_t *p = (uint8_t*) box_body(box); +sweep::do_sweep(rust_task *task, const std::set<void *> &marked) { + std::map<void *,const type_desc *>::iterator + begin(task->local_allocs.begin()), end(task->local_allocs.end()); + while (begin != end) { + void *alloc = begin->first; + + if (marked.find(alloc) == marked.end()) { + LOG(task, gc, "object is part of a cycle: %p", alloc); + + const type_desc *tydesc = begin->second; + uint8_t *p = reinterpret_cast<uint8_t *>(alloc); shape::arena arena; shape::type_param *params = shape::type_param::from_tydesc_and_data(tydesc, p, arena); sweep sweep(task, true, tydesc->shape, params, tydesc->shape_tables, - p); + p + sizeof(uintptr_t)); sweep.walk(); - boxed->free(box); + // FIXME: Run the destructor, *if* it's a resource. + task->free(alloc); } - box = box_next; + ++begin; } } void do_cc(rust_task *task) { - LOG(task, gc, "cc"); + LOG(task, gc, "cc; n allocs = %lu", + (long unsigned int)task->local_allocs.size()); irc_map ircs; irc::compute_ircs(task, ircs); - std::vector<rust_opaque_box*> roots; + std::vector<void *> roots; find_roots(task, ircs, roots); - std::set<rust_opaque_box*> marked; + std::set<void *> marked; mark::do_mark(task, roots, marked); sweep::do_sweep(task, marked); diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index 6fa049054ec..a31aa9b31af 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -231,36 +231,29 @@ struct rust_shape_tables { uint8_t *resources; }; -typedef unsigned long ref_cnt_t; - -// Corresponds to the boxed data in the @ region. The body follows the -// header; you can obtain a ptr via box_body() below. -struct rust_opaque_box { - ref_cnt_t ref_count; - type_desc *td; - rust_opaque_box *prev; - rust_opaque_box *next; -}; +struct rust_opaque_closure; // The type of functions that we spawn, which fall into two categories: // - the main function: has a NULL environment, but uses the void* arg // - unique closures of type fn~(): have a non-NULL environment, but // no arguments (and hence the final void*) is harmless -typedef void (*CDECL spawn_fn)(void*, rust_opaque_box*, void *); +typedef void (*CDECL spawn_fn)(void*, rust_opaque_closure*, void *); // corresponds to the layout of a fn(), fn@(), fn~() etc struct fn_env_pair { spawn_fn f; - rust_opaque_box *env; + rust_opaque_closure *env; }; -static inline void *box_body(rust_opaque_box *box) { - // Here we take advantage of the fact that the size of a box in 32 - // (resp. 64) bit is 16 (resp. 32) bytes, and thus always 16-byte aligned. - // If this were to change, we would have to update the method - // rustc::middle::trans::base::opaque_box_body() as well. - return (void*)(box + 1); -} +// corresponds the closures generated in trans_closure.rs +struct rust_opaque_closure { + intptr_t ref_count; + const type_desc *td; + // The size/types of these will vary per closure, so they + // cannot be statically expressed. See trans_closure.rs: + const type_desc *captured_tds[0]; + // struct bound_data; +}; struct type_desc { // First part of type_desc is known to compiler. diff --git a/src/rt/rust_shape.cpp b/src/rt/rust_shape.cpp index 875513ca264..37d8a42e95a 100644 --- a/src/rt/rust_shape.cpp +++ b/src/rt/rust_shape.cpp @@ -264,7 +264,7 @@ private: result = sub.result; } - inline void walk_box_contents2(cmp &sub, ptr_pair &box_dp) { + inline void walk_box_contents2(cmp &sub, ptr_pair &ref_count_dp) { sub.align = true; sub.walk(); result = sub.result; diff --git a/src/rt/rust_shape.h b/src/rt/rust_shape.h index 92f660db741..848e304df30 100644 --- a/src/rt/rust_shape.h +++ b/src/rt/rust_shape.h @@ -28,6 +28,7 @@ namespace shape { typedef unsigned long tag_variant_t; typedef unsigned long tag_align_t; +typedef unsigned long ref_cnt_t; // Constants @@ -375,6 +376,7 @@ ctxt<T>::walk() { case SHAPE_TAG: walk_tag0(); break; case SHAPE_BOX: walk_box0(); break; case SHAPE_STRUCT: walk_struct0(); break; + case SHAPE_OBJ: WALK_SIMPLE(walk_obj1); break; case SHAPE_RES: walk_res0(); break; case SHAPE_VAR: walk_var0(); break; case SHAPE_UNIQ: walk_uniq0(); break; @@ -589,6 +591,7 @@ public: default: abort(); } } + void walk_obj1() { DPRINT("obj"); } void walk_iface1() { DPRINT("iface"); } void walk_tydesc1(char kind) { @@ -642,6 +645,7 @@ public: void walk_uniq1() { sa.set(sizeof(void *), sizeof(void *)); } void walk_box1() { sa.set(sizeof(void *), sizeof(void *)); } void walk_fn1(char) { sa.set(sizeof(void *)*2, sizeof(void *)); } + void walk_obj1() { sa.set(sizeof(void *)*2, sizeof(void *)); } void walk_iface1() { sa.set(sizeof(void *), sizeof(void *)); } void walk_tydesc1(char) { sa.set(sizeof(void *), sizeof(void *)); } void walk_closure1(); @@ -850,8 +854,9 @@ protected: void walk_box_contents1(); void walk_uniq_contents1(); - void walk_fn_contents1(); - void walk_iface_contents1(); + void walk_fn_contents1(ptr &dp, bool null_td); + void walk_obj_contents1(ptr &dp); + void walk_iface_contents1(ptr &dp); void walk_variant1(tag_info &tinfo, tag_variant_t variant); static std::pair<uint8_t *,uint8_t *> get_vec_data_range(ptr dp); @@ -889,6 +894,13 @@ public: dp = next_dp; } + void walk_obj1() { + ALIGN_TO(alignof<void *>()); + U next_dp = dp + sizeof(void *) * 2; + static_cast<T *>(this)->walk_obj2(); + dp = next_dp; + } + void walk_iface1() { ALIGN_TO(alignof<void *>()); U next_dp = dp + sizeof(void *); @@ -934,17 +946,9 @@ template<typename T,typename U> void data<T,U>::walk_box_contents1() { typename U::template data<uint8_t *>::t box_ptr = bump_dp<uint8_t *>(dp); - U box_dp(box_ptr); - - // No need to worry about alignment so long as the box header is - // a multiple of 16 bytes. We can just find the body by adding - // the size of header to box_dp. - assert ((sizeof(rust_opaque_box) % 16) == 0 || - !"Must align to find the box body"); - - U body_dp = box_dp + sizeof(rust_opaque_box); - T sub(*static_cast<T *>(this), body_dp); - static_cast<T *>(this)->walk_box_contents2(sub, box_dp); + U ref_count_dp(box_ptr); + T sub(*static_cast<T *>(this), ref_count_dp + sizeof(ref_cnt_t)); + static_cast<T *>(this)->walk_box_contents2(sub, ref_count_dp); } template<typename T,typename U> @@ -1006,26 +1010,80 @@ data<T,U>::walk_tag1(tag_info &tinfo) { template<typename T,typename U> void -data<T,U>::walk_fn_contents1() { +data<T,U>::walk_fn_contents1(ptr &dp, bool null_td) { fn_env_pair pair = bump_dp<fn_env_pair>(dp); if (!pair.env) return; arena arena; const type_desc *closure_td = pair.env->td; - type_param *params = type_param::from_tydesc(closure_td, arena); - ptr closure_dp((uintptr_t)box_body(pair.env)); + type_param *params = + type_param::from_tydesc(closure_td, arena); + ptr closure_dp((uintptr_t)pair.env); T sub(*static_cast<T *>(this), closure_td->shape, params, closure_td->shape_tables, closure_dp); sub.align = true; + if (null_td) { + // if null_td flag is true, null out the type descr from + // the data structure while we walk. This is used in cycle + // collector when we are sweeping up data. The idea is that + // we are using the information in the embedded type desc to + // walk the contents, so we do not want to free it during that + // walk. This is not *strictly* necessary today because + // type_param::from_tydesc() actually pulls out the "shape" + // string and other information and copies it into a new + // location that is unaffected by the free. But it seems + // safer, particularly as this pulling out of information will + // not cope with nested, derived type descriptors. + pair.env->td = NULL; + } + + sub.walk(); + + if (null_td) { + pair.env->td = closure_td; + } +} + +template<typename T,typename U> +void +data<T,U>::walk_obj_contents1(ptr &dp) { + dp += sizeof(void *); // Skip over the vtable. + + uint8_t *box_ptr = bump_dp<uint8_t *>(dp); + type_desc *subtydesc = + *reinterpret_cast<type_desc **>(box_ptr + sizeof(void *)); + ptr obj_closure_dp(box_ptr + sizeof(void *)); + if (!box_ptr) // Null check. + return; + + arena arena; + type_param *params = type_param::from_obj_shape(subtydesc->shape, + obj_closure_dp, arena); + T sub(*static_cast<T *>(this), subtydesc->shape, params, + subtydesc->shape_tables, obj_closure_dp); + sub.align = true; sub.walk(); } template<typename T,typename U> void -data<T,U>::walk_iface_contents1() { - walk_box_contents1(); +data<T,U>::walk_iface_contents1(ptr &dp) { + uint8_t *box_ptr = bump_dp<uint8_t *>(dp); + if (!box_ptr) return; + U ref_count_dp(box_ptr); + uint8_t *body_ptr = box_ptr + sizeof(void*); + type_desc *valtydesc = + *reinterpret_cast<type_desc **>(body_ptr); + ptr value_dp(body_ptr + sizeof(void*) * 2); + // FIXME The 5 is a hard-coded way to skip over a struct shape + // header and the first two (number-typed) fields. This is too + // fragile, but I didn't see a good way to properly encode it. + T sub(*static_cast<T *>(this), valtydesc->shape + 5, NULL, NULL, + value_dp); + sub.align = true; + static_cast<T *>(this)->walk_box_contents2(sub, ref_count_dp); } // Polymorphic logging, for convenience @@ -1103,13 +1161,19 @@ private: void walk_fn2(char kind) { out << prefix << "fn"; prefix = ""; - data<log,ptr>::walk_fn_contents1(); + data<log,ptr>::walk_fn_contents1(dp, false); + } + + void walk_obj2() { + out << prefix << "obj"; + prefix = ""; + data<log,ptr>::walk_obj_contents1(dp); } void walk_iface2() { out << prefix << "iface("; prefix = ""; - data<log,ptr>::walk_iface_contents1(); + data<log,ptr>::walk_iface_contents1(dp); out << prefix << ")"; } diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 4acef16a209..86dfb3b666f 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -14,7 +14,6 @@ #include <algorithm> #include "globals.h" -#include "rust_upcall.h" // The amount of extra space at the end of each stack segment, available // to the rt, compiler and dynamic linker for running small functions @@ -247,7 +246,6 @@ rust_task::rust_task(rust_scheduler *sched, rust_task_list *state, running_on(-1), pinned_on(-1), local_region(&sched->srv->local_region), - boxed(&local_region), unwinding(false), killed(false), propagate_failure(true), @@ -297,7 +295,7 @@ rust_task::~rust_task() struct spawn_args { rust_task *task; spawn_fn f; - rust_opaque_box *envptr; + rust_opaque_closure *envptr; void *argptr; }; @@ -332,6 +330,8 @@ cleanup_task(cleanup_args *args) { } } +extern "C" void upcall_shared_free(void* ptr); + // This runs on the Rust stack extern "C" CDECL void task_start_wrapper(spawn_args *a) @@ -349,13 +349,12 @@ void task_start_wrapper(spawn_args *a) threw_exception = true; } - rust_opaque_box* env = a->envptr; + rust_opaque_closure* env = a->envptr; if(env) { - // free the environment (which should be a unique closure). + // free the environment. const type_desc *td = env->td; LOG(task, task, "Freeing env %p with td %p", env, td); - td->drop_glue(NULL, NULL, td->first_param, box_body(env)); - upcall_free_shared_type_desc(env->td); + td->drop_glue(NULL, NULL, td->first_param, env); upcall_shared_free(env); } @@ -368,7 +367,7 @@ void task_start_wrapper(spawn_args *a) void rust_task::start(spawn_fn spawnee_fn, - rust_opaque_box *envptr, + rust_opaque_closure *envptr, void *argptr) { LOG(this, task, "starting task from fn 0x%" PRIxPTR @@ -679,6 +678,38 @@ rust_port *rust_task::get_port_by_id(rust_port_id id) { return port; } + +// Temporary routine to allow boxes on one task's shared heap to be reparented +// to another. +const type_desc * +rust_task::release_alloc(void *alloc) { + I(sched, !lock.lock_held_by_current_thread()); + lock.lock(); + + assert(local_allocs.find(alloc) != local_allocs.end()); + const type_desc *tydesc = local_allocs[alloc]; + local_allocs.erase(alloc); + + local_region.release_alloc(alloc); + + lock.unlock(); + return tydesc; +} + +// Temporary routine to allow boxes from one task's shared heap to be +// reparented to this one. +void +rust_task::claim_alloc(void *alloc, const type_desc *tydesc) { + I(sched, !lock.lock_held_by_current_thread()); + lock.lock(); + + assert(local_allocs.find(alloc) == local_allocs.end()); + local_allocs[alloc] = tydesc; + local_region.claim_alloc(alloc); + + lock.unlock(); +} + void rust_task::notify(bool success) { // FIXME (1078) Do this in rust code diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 418eb54cab5..0d7cfed4a32 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -14,7 +14,6 @@ #include "rust_internal.h" #include "rust_kernel.h" #include "rust_obstack.h" -#include "boxed_region.h" // Corresponds to the rust chan (currently _chan) type. struct chan_handle { @@ -107,7 +106,6 @@ rust_task : public kernel_owned<rust_task>, rust_cond int pinned_on; memory_region local_region; - boxed_region boxed; // Indicates that fail() has been called and we are cleaning up. // We use this to suppress the "killed" flag during calls to yield. @@ -123,6 +121,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond rust_obstack dynastack; + std::map<void *,const type_desc *> local_allocs; uint32_t cc_counter; debug::task_debug_info debug; @@ -140,7 +139,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond ~rust_task(); void start(spawn_fn spawnee_fn, - rust_opaque_box *env, + rust_opaque_closure *env, void *args); void start(); bool running(); @@ -195,6 +194,11 @@ rust_task : public kernel_owned<rust_task>, rust_cond // not at all safe. intptr_t get_ref_count() const { return ref_count; } + // FIXME: These functions only exist to get the tasking system off the + // ground. We should never be migrating shared boxes between tasks. + const type_desc *release_alloc(void *alloc); + void claim_alloc(void *alloc, const type_desc *tydesc); + void notify(bool success); void *new_stack(size_t stk_sz, void *args_addr, size_t args_sz); diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 09ad8d930c6..65299a7d244 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -16,20 +16,6 @@ #include <stdint.h> -#ifdef __GNUC__ -#define LOG_UPCALL_ENTRY(task) \ - LOG(task, upcall, \ - "> UPCALL %s - task: %s 0x%" PRIxPTR \ - " retpc: x%" PRIxPTR, \ - __FUNCTION__, \ - (task)->name, (task), \ - __builtin_return_address(0)); -#else -#define LOG_UPCALL_ENTRY(task) \ - LOG(task, upcall, "> UPCALL task: %s @x%" PRIxPTR, \ - (task)->name, (task)); -#endif - // This is called to ensure we've set up our rust stacks // correctly. Strategically placed at entry to upcalls because they begin on // the rust stack and happen frequently enough to catch most stack changes, @@ -112,6 +98,7 @@ upcall_fail(char const *expr, struct s_malloc_args { uintptr_t retval; + size_t nbytes; type_desc *td; }; @@ -120,27 +107,31 @@ upcall_s_malloc(s_malloc_args *args) { rust_task *task = rust_scheduler::get_task(); LOG_UPCALL_ENTRY(task); - LOG(task, mem, "upcall malloc(0x%" PRIxPTR ")", args->td); + LOG(task, mem, + "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")", + args->nbytes, args->td); gc::maybe_gc(task); cc::maybe_cc(task); - // FIXME--does this have to be calloc? - rust_opaque_box *box = task->boxed.calloc(args->td); - void *body = box_body(box); + // TODO: Maybe use dladdr here to find a more useful name for the + // type_desc. + + void *p = task->malloc(args->nbytes, "tdesc", args->td); + memset(p, '\0', args->nbytes); - debug::maybe_track_origin(task, box); + task->local_allocs[p] = args->td; + debug::maybe_track_origin(task, p); LOG(task, mem, - "upcall malloc(0x%" PRIxPTR ") = box 0x%" PRIxPTR - " with body 0x%" PRIxPTR, - args->td, (uintptr_t)box, (uintptr_t)body); - args->retval = (uintptr_t) box; + "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ") = 0x%" PRIxPTR, + args->nbytes, args->td, (uintptr_t)p); + args->retval = (uintptr_t) p; } extern "C" CDECL uintptr_t -upcall_malloc(type_desc *td) { - s_malloc_args args = {0, td}; +upcall_malloc(size_t nbytes, type_desc *td) { + s_malloc_args args = {0, nbytes, td}; UPCALL_SWITCH_STACK(&args, upcall_s_malloc); return args.retval; } @@ -164,10 +155,10 @@ upcall_s_free(s_free_args *args) { "upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")", (uintptr_t)args->ptr, args->is_gc); + task->local_allocs.erase(args->ptr); debug::maybe_untrack_origin(task, args->ptr); - rust_opaque_box *box = (rust_opaque_box*) args->ptr; - task->boxed.free(box); + task->free(args->ptr, (bool) args->is_gc); } extern "C" CDECL void @@ -177,21 +168,6 @@ upcall_free(void* ptr, uintptr_t is_gc) { } /********************************************************************** - * Sanity checks on boxes, insert when debugging possible - * use-after-free bugs. See maybe_validate_box() in trans.rs. - */ - -extern "C" CDECL void -upcall_validate_box(rust_opaque_box* ptr) { - if (ptr) { - assert(ptr->ref_count > 0); - assert(ptr->td != NULL); - assert(ptr->td->align <= 8); - assert(ptr->td->size <= 4096); // might not really be true... - } -} - -/********************************************************************** * Allocate an object in the exchange heap. */ diff --git a/src/rt/rust_upcall.h b/src/rt/rust_upcall.h index 46676497d63..e5ee5bb6948 100644 --- a/src/rt/rust_upcall.h +++ b/src/rt/rust_upcall.h @@ -1,7 +1,17 @@ #pragma once -// Upcalls used from C code on occasion: +#ifdef __GNUC__ +#define LOG_UPCALL_ENTRY(task) \ + LOG(task, upcall, \ + "> UPCALL %s - task: %s 0x%" PRIxPTR \ + " retpc: x%" PRIxPTR, \ + __FUNCTION__, \ + (task)->name, (task), \ + __builtin_return_address(0)); +#else +#define LOG_UPCALL_ENTRY(task) \ + LOG(task, upcall, "> UPCALL task: %s @x%" PRIxPTR, \ + (task)->name, (task)); +#endif -extern "C" CDECL void upcall_shared_free(void* ptr); -extern "C" CDECL void upcall_free_shared_type_desc(type_desc *td); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 3701f73b0a8..50981882ecd 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -17,6 +17,7 @@ get_task_pointer get_time last_os_error leak +migrate_alloc nano_time new_port new_task @@ -62,7 +63,6 @@ upcall_dynastack_free upcall_dynastack_mark upcall_fail upcall_free -upcall_validate_box upcall_create_shared_type_desc upcall_free_shared_type_desc upcall_get_type_desc @@ -98,3 +98,4 @@ rust_uvtmp_read_start rust_uvtmp_timer rust_uvtmp_delete_buf rust_uvtmp_get_req_id + diff --git a/src/rt/test/rust_test_runtime.cpp b/src/rt/test/rust_test_runtime.cpp index 51ec9219396..815d55acd86 100644 --- a/src/rt/test/rust_test_runtime.cpp +++ b/src/rt/test/rust_test_runtime.cpp @@ -39,7 +39,7 @@ rust_domain_test::run() { return true; } -void task_entry(void *, rust_opaque_box*, void *) { +void task_entry(void *, rust_opaque_closure *, void *) { printf("task entry\n"); } |
