Skip to content

Commit

Permalink
Showing 26 changed files with 481 additions and 565 deletions.
4 changes: 0 additions & 4 deletions core/loader.rb
Original file line number Diff line number Diff line change
@@ -758,10 +758,6 @@ def epilogue

run_at_exits

@stage = "running object finalizers"
::GC.start
ObjectSpace.run_finalizers

flush_stdio

rescue Object => e
11 changes: 8 additions & 3 deletions core/object_space.rb
Original file line number Diff line number Diff line change
@@ -52,6 +52,14 @@ def self.each_object(what=nil, &block)
def self.define_finalizer(obj, prc=nil, &block)
prc ||= block

if obj.kind_of? ImmediateValue or obj.kind_of? Float
raise ArgumentError, "can't define finalizer for #{obj.class}"
end

if obj.frozen?
raise RuntimeError, "can't modify frozen #{obj.class}"
end

if obj.equal? prc
# This is allowed. This is the Rubinius specific API that calls
# __finalize__ when the object is finalized.
@@ -74,9 +82,6 @@ def self.undefine_finalizer(obj)
return obj
end

def self.run_finalizers
end

def self.garbage_collect
GC.start
end
2 changes: 0 additions & 2 deletions core/process.rb
Original file line number Diff line number Diff line change
@@ -83,8 +83,6 @@ def self.fork
end
end

ObjectSpace.run_finalizers

# Do not use Kernel.exit. This raises a SystemExit exception, which
# will run ensure blocks. This is not what MRI does and causes bugs
# in programs. See issue http://github.com/rubinius/rubinius/issues#issue/289 for
2 changes: 1 addition & 1 deletion machine/builtin/call_site.hpp
Original file line number Diff line number Diff line change
@@ -181,7 +181,7 @@ namespace rubinius {
cache->name(name);
cache->ip(ip);

state->memory()->needs_finalization(state, cache,
state->memory()->native_finalizer(state, cache,
(memory::FinalizerFunction)&CallSite::finalize);

state->vm()->metrics().machine.call_site_count++;
4 changes: 2 additions & 2 deletions machine/builtin/data.cpp
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ namespace rubinius {
data->internal(rdata);

if(mark || free) {
state->memory()->needs_finalization(state, data,
state->memory()->extension_finalizer(state, data,
(memory::FinalizerFunction)&Data::finalize);
}

@@ -73,7 +73,7 @@ namespace rubinius {
data->internal(rdata);

if(type->function.dmark || type->function.dfree) {
state->memory()->needs_finalization(state, data,
state->memory()->extension_finalizer(state, data,
(memory::FinalizerFunction)&Data::finalize);
}

5 changes: 2 additions & 3 deletions machine/builtin/dir.cpp
Original file line number Diff line number Diff line change
@@ -18,9 +18,8 @@ namespace rubinius {
Dir* d = Dir::allocate(state, G(dir));
d->os(0);

state->memory()->needs_finalization(state, d,
(memory::FinalizerFunction)&Dir::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, d,
(memory::FinalizerFunction)&Dir::finalize);

return d;
}
5 changes: 2 additions & 3 deletions machine/builtin/encoding.cpp
Original file line number Diff line number Diff line change
@@ -729,9 +729,8 @@ namespace rubinius {

c->converter(NULL);

state->memory()->needs_finalization(state, c,
(memory::FinalizerFunction)&Converter::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, c,
(memory::FinalizerFunction)&Converter::finalize);

return c;
}
7 changes: 3 additions & 4 deletions machine/builtin/ffi_pointer.cpp
Original file line number Diff line number Diff line change
@@ -115,14 +115,13 @@ namespace rubinius {

if(autorelease) {
if(!set_finalizer) {
state->memory()->needs_finalization(state, this,
(memory::FinalizerFunction)&Pointer::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, this,
(memory::FinalizerFunction)&Pointer::finalize);
set_finalizer = true;
}
} else {
if(set_finalizer) {
state->memory()->set_ruby_finalizer(this, cNil);
state->memory()->managed_finalizer(state, this, cNil);
set_finalizer = false;
}
}
10 changes: 4 additions & 6 deletions machine/builtin/fiber.cpp
Original file line number Diff line number Diff line change
@@ -38,9 +38,8 @@ namespace rubinius {
fib->data(state->vm()->new_fiber_data(true, fib->stack_size()->to_native()));
fib->data()->set_call_frame(state->vm()->call_frame());

state->memory()->needs_finalization(state, fib,
(memory::FinalizerFunction)&Fiber::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, fib,
(memory::FinalizerFunction)&Fiber::finalize);

state->vm()->current_fiber.set(fib);
state->vm()->root_fiber.set(fib);
@@ -119,9 +118,8 @@ namespace rubinius {

state->vm()->metrics().system.fibers_created++;

state->memory()->needs_finalization(state, fib,
(memory::FinalizerFunction)&Fiber::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, fib,
(memory::FinalizerFunction)&Fiber::finalize);

return fib;
#else
10 changes: 4 additions & 6 deletions machine/builtin/fsevent.cpp
Original file line number Diff line number Diff line change
@@ -34,9 +34,8 @@ namespace rubinius {
if(fsevent->kq() < 0) {
logger::error("%s: unable to create kqueue", strerror(errno));
} else {
state->memory()->needs_finalization(state, fsevent,
(memory::FinalizerFunction)&FSEvent::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, fsevent,
(memory::FinalizerFunction)&FSEvent::finalize);
}

return fsevent;
@@ -84,9 +83,8 @@ namespace rubinius {
if(fsevent->in() < 0) {
logger::error("%s: unable to create inotify", strerror(errno));
} else {
state->memory()->needs_finalization(state, fsevent,
(memory::FinalizerFunction)&FSEvent::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, fsevent,
(memory::FinalizerFunction)&FSEvent::finalize);
}

return fsevent;
4 changes: 2 additions & 2 deletions machine/builtin/io.cpp
Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@ namespace rubinius {

// Don't bother to add finalization for stdio
if(fd >= 3) {
state->memory()->needs_finalization(state, io,
state->memory()->extension_finalizer(state, io,
(memory::FinalizerFunction)&IO::finalize);
}

@@ -86,7 +86,7 @@ namespace rubinius {
IO* io = state->memory()->new_object<IO>(state, as<Class>(self));
io->ibuffer(state, IOBuffer::create(state));

state->memory()->needs_finalization(state, io,
state->memory()->extension_finalizer(state, io,
(memory::FinalizerFunction)&IO::finalize);

return io;
2 changes: 1 addition & 1 deletion machine/builtin/system.cpp
Original file line number Diff line number Diff line change
@@ -1510,7 +1510,7 @@ namespace rubinius {

Object* System::vm_set_finalizer(STATE, Object* obj, Object* fin) {
if(!obj->reference_p()) return cFalse;
state->memory()->set_ruby_finalizer(obj, fin);
state->memory()->managed_finalizer(state, obj, fin);
return cTrue;
}

5 changes: 2 additions & 3 deletions machine/builtin/thread.cpp
Original file line number Diff line number Diff line change
@@ -90,9 +90,8 @@ namespace rubinius {

thr->function(function);

state->memory()->needs_finalization(state, thr,
(memory::FinalizerFunction)&Thread::finalize,
memory::FinalizeObject::eUnmanaged);
state->memory()->native_finalizer(state, thr,
(memory::FinalizerFunction)&Thread::finalize);

state->vm()->metrics().system.threads_created++;

2 changes: 1 addition & 1 deletion machine/environment.cpp
Original file line number Diff line number Diff line change
@@ -551,7 +551,7 @@ namespace rubinius {

shared->thread_nexus()->lock(state->vm());

shared->finalizer_handler()->finish(state);
shared->finalizer()->finish(state);

NativeMethod::cleanup_thread(state);

2 changes: 1 addition & 1 deletion machine/machine_threads.cpp
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ namespace rubinius {

MachineThread::MachineThread(STATE, std::string name, StackSize stack_size)
: vm_(state->shared().thread_nexus()->new_vm(&state->shared(), name.c_str()))
, thread_running_(false)
, stack_size_(stack_size)
, thread_running_(false)
, thread_exit_(false)
{
state->shared().machine_threads()->register_thread(this);
5 changes: 3 additions & 2 deletions machine/machine_threads.hpp
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@

#include "util/thread.hpp"

#include <atomic>
#include <string>
#include <list>

@@ -16,12 +17,12 @@ namespace rubinius {

class MachineThread {
VM* vm_;
bool thread_running_;
uint32_t stack_size_;

protected:

bool thread_exit_;
std::atomic<bool> thread_running_;
std::atomic<bool> thread_exit_;

public:

24 changes: 0 additions & 24 deletions machine/memory.cpp
Original file line number Diff line number Diff line change
@@ -523,10 +523,6 @@ namespace rubinius {

if(!collect_young_flag_ && !collect_full_flag_) return;

if(memory::FinalizerThread* finalizer = state->shared().finalizer_handler()) {
finalizer->start_collection(state);
}

if(cDebugThreading) {
std::cerr << std::endl << "[" << state
<< " WORLD beginning GC.]" << std::endl;
@@ -584,10 +580,6 @@ namespace rubinius {
#ifdef RBX_GC_DEBUG
young_->verify(data);
#endif
if(memory::FinalizerThread* finalizer = state->shared().finalizer_handler()) {
finalizer->finish_collection(state);
}
*/

collect_young_flag_ = false;
@@ -666,10 +658,6 @@ namespace rubinius {
metrics.gc.immix_count++;
metrics.gc.large_count++;

if(memory::FinalizerThread* finalizer = state->shared().finalizer_handler()) {
finalizer->finish_collection(state);
}

collect_full_flag_ = false;
interrupt_flag_ = false;

@@ -846,18 +834,6 @@ namespace rubinius {
}
}

void Memory::needs_finalization(STATE, Object* obj, memory::FinalizerFunction func,
memory::FinalizeObject::FinalizeKind kind)
{
if(memory::FinalizerThread* finalizer = shared_.finalizer_handler()) {
finalizer->record(state, obj, func, kind);
}
}

void Memory::set_ruby_finalizer(Object* obj, Object* finalizer) {
shared_.finalizer_handler()->set_ruby_finalizer(obj, finalizer);
}

capi::Handle* Memory::add_capi_handle(STATE, Object* obj) {
if(!obj->reference_p()) {
rubinius::bug("Trying to add a handle for a non reference");
24 changes: 19 additions & 5 deletions machine/memory.hpp
Original file line number Diff line number Diff line change
@@ -223,8 +223,8 @@ namespace rubinius {
}
}

memory::FinalizerThread* finalizer_handler() const {
return shared_.finalizer_handler();
memory::FinalizerThread* finalizer() const {
return shared_.finalizer();
}

memory::InflatedHeaders* inflated_headers() const {
@@ -550,9 +550,23 @@ namespace rubinius {

void collect_maybe(STATE);

void needs_finalization(STATE, Object* obj, memory::FinalizerFunction func,
memory::FinalizeObject::FinalizeKind kind = memory::FinalizeObject::eManaged);
void set_ruby_finalizer(Object* obj, Object* finalizer);
void native_finalizer(STATE, Object* obj, memory::FinalizerFunction func) {
if(memory::FinalizerThread* f = this->finalizer()) {
f->native_finalizer(state, obj, func);
}
}

void extension_finalizer(STATE, Object* obj, memory::FinalizerFunction func) {
if(memory::FinalizerThread* f = this->finalizer()) {
f->extension_finalizer(state, obj, func);
}
}

void managed_finalizer(STATE, Object* obj, Object* finalizer) {
if(memory::FinalizerThread* f = this->finalizer()) {
f->managed_finalizer(state, obj, finalizer);
}
}

InflatedHeader* inflate_header(STATE, ObjectHeader* obj);
void inflate_for_id(STATE, ObjectHeader* obj, uint32_t id);
577 changes: 226 additions & 351 deletions machine/memory/finalizer.cpp

Large diffs are not rendered by default.

220 changes: 114 additions & 106 deletions machine/memory/finalizer.hpp
Original file line number Diff line number Diff line change
@@ -5,131 +5,139 @@

#include "memory/root.hpp"

#include <atomic>
#include <condition_variable>
#include <list>
#include <mutex>

namespace rubinius {
class VM;
class State;
struct CallFrame;
class Memory;
class Object;
struct CallFrame;

namespace memory {
typedef void (*FinalizerFunction)(STATE, Object*);
namespace memory {
class ImmixGC;

struct FinalizeObject {
public:
enum FinalizeKind {
eManaged,
eUnmanaged
};
typedef void (*FinalizerFunction)(STATE, Object*);

class FinalizerObject {
Object* object_;

enum FinalizationStatus {
eLive,
eQueued,
eRubyFinalized,
eNativeFinalized,
eReleased
public:
FinalizerObject(STATE, Object* object)
: object_(object)
{ }
virtual ~FinalizerObject() { }

Object* object() const {
return object_;
}

void object(Object* obj) {
object_ = obj;
}

virtual void finalize(STATE) = 0;
virtual void mark(ImmixGC* gc) = 0;
virtual bool match_p(STATE, Object* object, Object* finalizer) = 0;
};

public:
FinalizeObject()
: object(NULL)
, kind(eManaged)
, status(eLive)
, finalizer(0)
, ruby_finalizer(0)
{}

Object* object;
FinalizeKind kind;
FinalizationStatus status;
FinalizerFunction finalizer;
Object* ruby_finalizer;

void queued() {
status = eQueued;
}

bool queued_p() const {
return status == eQueued;
}
};

typedef std::list<FinalizeObject> FinalizeObjects;
typedef std::list<FinalizeObjects*> FinalizeObjectsList;

class FinalizerThread : public MachineThread {
public:
class iterator {
FinalizerThread* handler_;
FinalizeObjects* current_list_;
FinalizeObjects::iterator end_;
FinalizeObjects::iterator current_;
FinalizeObjectsList::iterator lists_iterator_;
class NativeFinalizer : public FinalizerObject {
FinalizerFunction finalizer_;

public:
iterator(FinalizerThread* fh);
~iterator() {}
NativeFinalizer(STATE, Object* object, FinalizerFunction finalizer)
: FinalizerObject(state, object)
, finalizer_(finalizer)
{ }

void finalize(STATE);
void mark(ImmixGC* gc);
bool match_p(STATE, Object* object, Object* finalizer) { return false; }
};

class ExtensionFinalizer : public FinalizerObject {
FinalizerFunction finalizer_;

void next(bool live);
bool end();
FinalizeObject& current() { return *current_; }
public:
ExtensionFinalizer(STATE, Object* object, FinalizerFunction finalizer)
: FinalizerObject(state, object)
, finalizer_(finalizer)
{ }

void finalize(STATE);
void mark(ImmixGC* gc);
bool match_p(STATE, Object* object, Object* finalizer) { return false; }
};

friend class iterator;
class ManagedFinalizer : public FinalizerObject {
Object* finalizer_;

enum ProcessItemKind {
eRuby,
eNative,
eRelease
public:
ManagedFinalizer(STATE, Object* object, Object* finalizer)
: FinalizerObject(state, object)
, finalizer_(finalizer)
{ }

void finalize(STATE);
void mark(ImmixGC* gc);
bool match_p(STATE, Object* object, Object* finalizer);
};

private:
FinalizeObjectsList* lists_;
FinalizeObjects* live_list_;
FinalizeObjects* process_list_;
FinalizeObjects::iterator process_item_;
FinalizerThread::iterator* iterator_;
ProcessItemKind process_item_kind_;
utilities::thread::Mutex live_guard_;
utilities::thread::Mutex worker_lock_;
utilities::thread::Condition worker_cond_;
utilities::thread::Mutex supervisor_lock_;
utilities::thread::Condition supervisor_cond_;
bool finishing_;

public:

FinalizerThread(STATE);
virtual ~FinalizerThread();

void finalize(STATE);
void first_process_item();
void next_process_item();
void finish(STATE);

void record(STATE, Object* obj, FinalizerFunction func,
FinalizeObject::FinalizeKind kind);
void set_ruby_finalizer(Object* obj, Object* finalizer);

void queue_objects(STATE);

void start_collection(STATE);
void finish_collection(STATE);

FinalizerThread::iterator& begin();

void worker_signal();
void worker_wait();
void supervisor_signal();
void supervisor_wait();

void initialize(STATE);
void run(STATE);
void stop(STATE);
void wakeup(STATE);
};
}
typedef std::list<FinalizerObject*> FinalizerObjects;

class FinalizerThread : public MachineThread {
class Synchronization {
std::mutex list_mutex_;
std::condition_variable list_condition_;

public:
Synchronization()
: list_mutex_()
, list_condition_()
{ }

std::mutex& list_mutex() {
return list_mutex_;
}

std::condition_variable& list_condition() {
return list_condition_;
}
};

FinalizerObjects live_list_;
FinalizerObjects process_list_;

Synchronization* synchronization_;

std::atomic<bool> finishing_;

public:
FinalizerThread(STATE);
virtual ~FinalizerThread();

void finish(STATE);

void native_finalizer(STATE, Object* obj, FinalizerFunction func);
void extension_finalizer(STATE, Object* obj, FinalizerFunction func);
void managed_finalizer(STATE, Object* obj, Object* finalizer);

void add_finalizer(STATE, FinalizerObject* obj);

void gc_scan(ImmixGC* gc, Memory* memory);

void initialize(STATE);
void run(STATE);
void stop(STATE);
void wakeup(STATE);
void after_fork_child(STATE);

void cleanup();
};
}
}

#endif
24 changes: 2 additions & 22 deletions machine/memory/immix_collector.cpp
Original file line number Diff line number Diff line change
@@ -390,28 +390,8 @@ namespace memory {
}

void ImmixGC::walk_finalizers() {
FinalizerThread* fh = memory_->finalizer_handler();
if(!fh) return;

for(FinalizerThread::iterator i = fh->begin();
!i.end();
/* advance is handled in the loop */)
{
FinalizeObject& fi = i.current();

bool live = fi.object->marked_p(memory_->mark());

if(fi.ruby_finalizer) {
if(Object* fwd = saw_object(fi.ruby_finalizer)) {
fi.ruby_finalizer = fwd;
}
}

if(Object* fwd = saw_object(fi.object)) {
fi.object = fwd;
}

i.next(live);
if(FinalizerThread* f = memory_->finalizer()) {
f->gc_scan(this, memory_);
}
}
}
2 changes: 1 addition & 1 deletion machine/shared_state.cpp
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ namespace rubinius {
: thread_nexus_(new ThreadNexus())
, machine_threads_(NULL)
, signals_(NULL)
, finalizer_thread_(NULL)
, finalizer_(NULL)
, console_(NULL)
, metrics_(NULL)
, diagnostics_(NULL)
10 changes: 5 additions & 5 deletions machine/shared_state.hpp
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ namespace rubinius {
ThreadNexus* thread_nexus_;
MachineThreads* machine_threads_;
SignalThread* signals_;
memory::FinalizerThread* finalizer_thread_;
memory::FinalizerThread* finalizer_;
console::Console* console_;
metrics::Metrics* metrics_;
diagnostics::Diagnostics* diagnostics_;
@@ -158,12 +158,12 @@ namespace rubinius {
return machine_threads_;
}

memory::FinalizerThread* finalizer_handler() const {
return finalizer_thread_;
memory::FinalizerThread* finalizer() const {
return finalizer_;
}

void set_finalizer_handler(memory::FinalizerThread* thr) {
finalizer_thread_ = thr;
void set_finalizer(memory::FinalizerThread* thr) {
finalizer_ = thr;
}

Array* vm_threads(STATE);
5 changes: 2 additions & 3 deletions machine/thread_nexus.cpp
Original file line number Diff line number Diff line change
@@ -243,9 +243,8 @@ namespace rubinius {

{
std::unique_lock<std::mutex> lk(wait_mutex_);
while(phase_flag_.load(std::memory_order_acquire) != 0) {
wait_condition_.wait(lk);
}
wait_condition_.wait(lk,
[this]{ return phase_flag_.load(std::memory_order_acquire) == 0; });
}

id = 0;
14 changes: 14 additions & 0 deletions machine/thread_nexus.hpp
Original file line number Diff line number Diff line change
@@ -97,6 +97,20 @@ namespace rubinius {
bool blocking_p(VM* vm);
bool yielding_p(VM* vm);

void yield(VM* vm) {
while(stop_p()) {
waiting_phase(vm);

{
std::unique_lock<std::mutex> lk(wait_mutex_);
wait_condition_.wait(lk,
[this]{ return !stop_.load(std::memory_order_acquire); });
}

managed_phase(vm);
}
}

bool waiting_lock(VM* vm);

bool try_lock(VM* vm) {
66 changes: 62 additions & 4 deletions spec/ruby/core/objectspace/define_finalizer_spec.rb
Original file line number Diff line number Diff line change
@@ -6,15 +6,45 @@
# It is highly questionable whether these aspects of ObjectSpace
# should be spec'd at all.
describe "ObjectSpace.define_finalizer" do
it "raises a RuntimeError if the object is not garbage collectable" do
lambda {
it "raises an ArgumentError if the object is a Symbol" do
lambda do
ObjectSpace.define_finalizer(:symbol, lambda { })
}.should raise_error(RuntimeError)
end.should raise_error(ArgumentError)
end

it "raises an ArgumentError if the object is a Fixnum" do
lambda do
ObjectSpace.define_finalizer(1, lambda { })
end.should raise_error(ArgumentError)
end

it "raises an RuntimeError if the object is a Bignum" do
lambda do
ObjectSpace.define_finalizer(bignum_value(), lambda { })
end.should raise_error(RuntimeError)
end

it "raises an ArgumentError if the object is a Float" do
lambda do
ObjectSpace.define_finalizer(3.5, lambda { })
end.should raise_error(ArgumentError)
end

it "raises an ArgumentError if the object is a nil" do
lambda do
ObjectSpace.define_finalizer(nil, lambda { })
end.should raise_error(ArgumentError)
end

it "raises a RuntimeError if the object is frozen" do
lambda do
ObjectSpace.define_finalizer("abc".freeze, lambda { })
end.should raise_error(RuntimeError)
end

it "raises an ArgumentError if the action does not respond to call" do
lambda {
ObjectSpace.define_finalizer("", 3)
ObjectSpace.define_finalizer("", mock("ObjectSpace.define_finalizer no #call"))
}.should raise_error(ArgumentError)
end

@@ -60,5 +90,33 @@ def handler.call(obj) end
exit 0
end
end

# These specs are defined under the fork specs because there is no
# deterministic way to force finalizers to be run, except process exit, so
# we rely on that.
it "allows multiple finalizers with different 'callables' to be defined" do
rd1, wr1 = IO.pipe
rd2, wr2 = IO.pipe

if Kernel::fork then
wr1.close
wr2.close

rd1.read.should == "finalized1"
rd2.read.should == "finalized2"

rd1.close
rd2.close
else
rd1.close
rd2.close
obj = mock("ObjectSpace.define_finalizer multiple")

ObjectSpace.define_finalizer(obj, Proc.new { wr1.write "finalized1"; wr1.close })
ObjectSpace.define_finalizer(obj, Proc.new { wr2.write "finalized2"; wr2.close })

exit 0
end
end
end
end

0 comments on commit 26403b2

Please sign in to comment.