Permalink
Browse files

Return of tracking the fiber data explicitly

This mostly reverts 1e19933 and
introduces a separate mechanism for marking fiber data's. This marking
is used so we can clean up inactive fibers that are unreachable.

The reason for reverting the original change is not because it's wrong,
but because it makes concurrent gc much harder. The problem is that it
was scanning stacks inside the fiber marking, which is a problem with
concurrent marking.

Therefore we now use a mechanism where we can scan the not running but
still active fibers in the final phase of the mature GC, so we can do
that in the stop the world phases of concurrent GC.

The downside of this approach compared to the previous one is that
inactive young fibers will be scanned during young gc cycles and kept
active until promoted. Only after that will they be cleaned up. This
only happens for fibers that haven't finished and are not reachable
anymore, so this is not a huge issue.
  • Loading branch information...
1 parent 5c16d39 commit b3579cfd46fb773b563e0f66154187dc32f31909 @dbussink dbussink committed Jun 3, 2013
Showing with 145 additions and 35 deletions.
  1. +5 −15 vm/builtin/fiber.cpp
  2. +2 −0 vm/fiber_data.cpp
  3. +20 −1 vm/fiber_data.hpp
  4. +50 −6 vm/fiber_stack.cpp
  5. +13 −1 vm/fiber_stack.hpp
  6. +8 −9 vm/gc/baker.cpp
  7. +12 −0 vm/gc/gc.cpp
  8. +1 −0 vm/gc/gc.hpp
  9. +1 −2 vm/gc/immix.cpp
  10. +13 −0 vm/objectmemory.cpp
  11. +1 −0 vm/objectmemory.hpp
  12. +9 −1 vm/vm.cpp
  13. +10 −0 vm/vm.hpp
View
@@ -34,7 +34,8 @@ namespace rubinius {
fib->locals(state, nil<LookupTable>());
fib->root_ = true;
fib->status_ = Fiber::eRunning;
- fib->data_ = new FiberData(state->vm(), true);
+
+ fib->data_ = state->vm()->new_fiber_data(true);
state->memory()->needs_finalization(fib, (FinalizerFunction)&Fiber::finalize);
@@ -120,7 +121,7 @@ namespace rubinius {
Object* Fiber::resume(STATE, Arguments& args, CallFrame* calling_environment) {
#ifdef RBX_FIBER_ENABLED
if(!data_) {
- data_ = new FiberData(state->vm());
+ data_ = state->vm()->new_fiber_data();
}
if(status_ == Fiber::eDead || data_->dead_p()) {
@@ -178,7 +179,7 @@ namespace rubinius {
Object* Fiber::transfer(STATE, Arguments& args, CallFrame* calling_environment) {
#ifdef RBX_FIBER_ENABLED
if(!data_) {
- data_ = new FiberData(state->vm());
+ data_ = state->vm()->new_fiber_data();
}
if(status_ == Fiber::eDead || data_->dead_p()) {
@@ -290,20 +291,9 @@ namespace rubinius {
void Fiber::Info::mark(Object* obj, ObjectMark& mark) {
auto_mark(obj, mark);
Fiber* fib = force_as<Fiber>(obj);
-
FiberData* data = fib->data_;
if(!data || data->dead_p()) return;
-
- AddressDisplacement dis(data->data_offset(),
- data->data_lower_bound(),
- data->data_upper_bound());
-
- if(CallFrame* cf = data->call_frame()) {
- mark.gc->walk_call_frame(cf, &dis);
- }
-
- mark.gc->scan(data->variable_root_buffers(), false, &dis);
-
+ data->set_mark();
}
}
View
@@ -110,6 +110,8 @@ namespace rubinius {
FiberData::~FiberData() {
if(heap_) free(heap_);
+ if(!thread_) return;
+ thread_->remove_fiber_data(this);
}
void FiberData::take_stack(STATE) {
View
@@ -51,6 +51,8 @@ namespace rubinius {
eDead
} status_;
+ bool mark_;
+
VM* thread_;
FiberStack* stack_;
@@ -62,9 +64,11 @@ namespace rubinius {
CallFrame* call_frame_;
- public:
+ // Private constructor so only FiberStack can use it.
+
FiberData(VM* thread, bool root=false)
: status_(root ? eOnStack : eInitial)
+ , mark_(true)
, thread_(thread)
, stack_(0)
, heap_(0)
@@ -73,12 +77,27 @@ namespace rubinius {
, call_frame_(0)
{}
+ friend class FiberStacks;
+
+ public:
~FiberData();
bool dead_p() const {
return status_ == eDead;
}
+ bool marked_p() const {
+ return mark_;
+ }
+
+ void set_mark() {
+ mark_ = true;
+ }
+
+ void clear_mark() {
+ mark_ = false;
+ }
+
CallFrame* call_frame() const {
return call_frame_;
}
View
@@ -57,21 +57,58 @@ namespace rubinius {
dec_ref();
}
- FiberStacks::FiberStacks(SharedState& shared)
+ FiberStacks::FiberStacks(VM* thread, SharedState& shared)
: max_stacks_(shared.config.fiber_stacks)
, stack_size_(shared.config.fiber_stack_size)
+ , thread_(thread)
, trampoline_(0)
- {}
+ {
+ lock_.init();
+ }
FiberStacks::~FiberStacks() {
- for(Stacks::iterator i = stacks_.begin();
- i != stacks_.end();
- ++i)
- {
+ for(Datas::iterator i = datas_.begin(); i != datas_.end(); ++i) {
+ (*i)->die();
+ }
+
+ for(Stacks::iterator i = stacks_.begin(); i != stacks_.end(); ++i) {
i->free();
}
}
+ void FiberStacks::gc_scan(GarbageCollector* gc, bool marked_only) {
+ for(Datas::iterator i = datas_.begin(); i != datas_.end(); ++i) {
+ FiberData* data = *i;
+ if(data->dead_p()) continue;
+ if(marked_only && !data->marked_p()) {
+ data->status_ = FiberData::eDead;
+ continue;
+ }
+
+ AddressDisplacement dis(data->data_offset(),
+ data->data_lower_bound(),
+ data->data_upper_bound());
+
+ if(CallFrame* cf = data->call_frame()) {
+ gc->walk_call_frame(cf, &dis);
+ }
+
+ gc->scan(data->variable_root_buffers(), false, &dis);
+ }
+ }
+
+ FiberData* FiberStacks::new_data(bool root) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
+ FiberData* data = new FiberData(thread_, root);
+ datas_.insert(data);
+ return data;
+ }
+
+ void FiberStacks::remove_data(FiberData* data) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
+ datas_.erase(data);
+ }
+
FiberStack* FiberStacks::allocate() {
for(Stacks::iterator i = stacks_.begin();
i != stacks_.end();
@@ -115,4 +152,11 @@ namespace rubinius {
return trampoline_;
}
+
+ void FiberStacks::gc_clear_mark() {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
+ for(Datas::iterator i = datas_.begin(); i != datas_.end(); ++i) {
+ (*i)->clear_mark();
+ }
+ }
}
View
@@ -1,6 +1,7 @@
#ifndef RBX_VM_FIBER_STACK_HPP
#define RBX_VM_FIBER_STACK_HPP
+#include <tr1/unordered_set>
#include "util/thread.hpp"
namespace rubinius {
@@ -71,20 +72,31 @@ namespace rubinius {
private:
typedef std::list<FiberStack> Stacks;
+ typedef std::tr1::unordered_set<FiberData*> Datas;
size_t max_stacks_;
size_t stack_size_;
+ VM* thread_;
Stacks stacks_;
+ Datas datas_;
void* trampoline_;
+ utilities::thread::SpinLock lock_;
public:
- FiberStacks(SharedState& shared);
+ FiberStacks(VM* thread, SharedState& shared);
~FiberStacks();
FiberStack* allocate();
+ void remove_data(FiberData* data);
+
+ FiberData* new_data(bool root=false);
+
void* trampoline();
+
+ void gc_clear_mark();
+ void gc_scan(GarbageCollector* gc, bool marked_only = true);
};
}
View
@@ -235,15 +235,14 @@ namespace rubinius {
// objects kept alive for finalization through weakrefs.
clean_weakrefs(true);
- // Objects with finalizers must be kept alive until the finalizers have
- // run.
- walk_finalizers();
-
- // Process possible promotions from processing objects with finalizers.
- handle_promotions();
-
- if(!promoted_stack_.empty()) rubinius::bug("promote stack has elements!");
- if(!fully_scanned_p()) rubinius::bug("more young refs");
+ do {
+ // Objects with finalizers must be kept alive until the finalizers have
+ // run.
+ walk_finalizers();
+ // Scan any fibers that aren't running but still active
+ scan_fibers(data, false);
+ handle_promotions();
+ } while(!promoted_stack_.empty() && !fully_scanned_p());
// Remove unreachable locked objects still in the list
if(data.threads()) {
View
@@ -396,6 +396,18 @@ namespace rubinius {
}
}
+ void GarbageCollector::scan_fibers(GCData& data, bool marked_only) {
+ if(data.threads()) {
+ for(std::list<ManagedThread*>::iterator i = data.threads()->begin();
+ i != data.threads()->end();
+ ++i) {
+ if(VM* vm = (*i)->as_vm()) {
+ vm->gc_fiber_scan(this, marked_only);
+ }
+ }
+ }
+ }
+
void GarbageCollector::clean_weakrefs(bool check_forwards) {
if(!weak_refs_) return;
View
@@ -166,6 +166,7 @@ namespace rubinius {
return obj;
}
+ void scan_fibers(GCData& data, bool marked_only = true);
void clean_weakrefs(bool check_forwards=false);
void clean_locked_objects(ManagedThread* thr, bool young_only);
View
@@ -189,7 +189,6 @@ namespace rubinius {
gc_.process_mark_stack(allocator_);
- // We've now finished marking the entire object graph.
// Clean weakrefs before keeping additional objects alive
// for finalization, so people don't get a hold of finalized
// objects through weakrefs.
@@ -199,9 +198,9 @@ namespace rubinius {
// live, so we must check the mark_stack again.
do {
walk_finalizers();
+ scan_fibers(data, true);
} while(gc_.process_mark_stack(allocator_));
-
// Remove unreachable locked objects still in the list
if(data.threads()) {
for(std::list<ManagedThread*>::iterator i = data.threads()->begin();
View
@@ -631,6 +631,7 @@ namespace rubinius {
#endif
code_manager_.clear_marks();
+ clear_fiber_marks(data.threads());
immix_->reset_stats();
@@ -721,6 +722,18 @@ namespace rubinius {
handles->deallocate_handles(cached, mark(), young);
}
+ void ObjectMemory::clear_fiber_marks(std::list<ManagedThread*>* threads) {
+ if(threads) {
+ for(std::list<ManagedThread*>::iterator i = threads->begin();
+ i != threads->end();
+ ++i) {
+ if(VM* vm = (*i)->as_vm()) {
+ vm->gc_fiber_clear_mark();
+ }
+ }
+ }
+ }
+
size_t ObjectMemory::mature_bytes_allocated() {
return immix_->bytes_allocated() + mark_sweep_->allocated_bytes;
}
View
@@ -333,6 +333,7 @@ namespace rubinius {
void validate_handles(capi::Handles* handles);
void prune_handles(capi::Handles* handles, std::list<capi::Handle*>* cached, BakerGC* young);
+ void clear_fiber_marks(std::list<ManagedThread*>* threads);
ObjectPosition validate_object(Object* obj);
bool valid_young_object_p(Object* obj);
View
@@ -69,7 +69,7 @@ namespace rubinius {
: ManagedThread(id, shared, ManagedThread::eRuby)
, saved_call_frame_(0)
, saved_call_site_information_(0)
- , fiber_stacks_(shared)
+ , fiber_stacks_(this, shared)
, park_(new Park)
, run_signals_(false)
, shared(shared)
@@ -450,6 +450,14 @@ namespace rubinius {
shared.tool_broker()->at_gc(&ls);
}
+ void VM::gc_fiber_clear_mark() {
+ fiber_stacks_.gc_clear_mark();
+ }
+
+ void VM::gc_fiber_scan(GarbageCollector* gc, bool only_marked) {
+ fiber_stacks_.gc_scan(gc, only_marked);
+ }
+
void VM::gc_verify(GarbageCollector* gc) {
if(CallFrame* cf = saved_call_frame()) {
gc->verify_call_frame(cf);
View
@@ -318,6 +318,14 @@ namespace rubinius {
return fiber_stacks_.trampoline();
}
+ FiberData* new_fiber_data(bool root=false) {
+ return fiber_stacks_.new_data(root);
+ }
+
+ void remove_fiber_data(FiberData* data) {
+ fiber_stacks_.remove_data(data);
+ }
+
VariableRootBuffers& current_root_buffers();
public:
@@ -434,6 +442,8 @@ namespace rubinius {
void register_kill(STATE);
void gc_scan(GarbageCollector* gc);
+ void gc_fiber_clear_mark();
+ void gc_fiber_scan(GarbageCollector* gc, bool only_marked = true);
void gc_verify(GarbageCollector* gc);
};

0 comments on commit b3579cf

Please sign in to comment.