Permalink
Browse files

Setup finalizer thread as an auxiliary thread

We were lazy starting the finalizer thread directly from the object
memory. This refactors it so that we use the auxiliary thread mechanism
we also use for other supporting threads such as the signal thread.
  • Loading branch information...
1 parent ff5e598 commit f0a86ed44c6378d0f78c8f7898b8c6fe99fbaad8 @dbussink dbussink committed Jan 7, 2013
Showing with 270 additions and 114 deletions.
  1. +11 −3 vm/environment.cpp
  2. +4 −1 vm/environment.hpp
  3. +171 −0 vm/finalizer.cpp
  4. +56 −0 vm/finalizer.hpp
  5. +5 −0 vm/gc/finalize.hpp
  6. +5 −90 vm/objectmemory.cpp
  7. +1 −13 vm/objectmemory.hpp
  8. +1 −0 vm/shared_state.cpp
  9. +10 −1 vm/shared_state.hpp
  10. +6 −6 vm/signal.cpp
View
@@ -32,6 +32,7 @@
#endif
#include "signal.hpp"
+#include "finalizer.hpp"
#include "object_utils.hpp"
#include "inline_cache.hpp"
@@ -82,7 +83,8 @@ namespace rubinius {
, argv_(argv)
, signature_(0)
, version_(0)
- , sig_handler_(NULL)
+ , signal_handler_(NULL)
+ , finalizer_handler_(NULL)
{
#ifdef ENABLE_LLVM
if(!llvm::llvm_start_multithreaded()) {
@@ -133,7 +135,8 @@ namespace rubinius {
}
Environment::~Environment() {
- delete sig_handler_;
+ delete signal_handler_;
+ delete finalizer_handler_;
VM::discard(state, root_vm);
SharedState::discard(shared);
@@ -312,7 +315,7 @@ namespace rubinius {
#endif
state->vm()->set_run_signals(true);
- sig_handler_ = new SignalHandler(state);
+ signal_handler_ = new SignalHandler(state);
#ifndef RBX_WINDOWS
// Ignore sigpipe.
@@ -349,6 +352,10 @@ namespace rubinius {
signal(SIGTERM, quit_handler);
}
+ void Environment::start_finalizer() {
+ finalizer_handler_ = new FinalizerHandler(state);
+ }
+
void Environment::load_vm_options(int argc, char**argv) {
/* Parse -X options from RBXOPT environment variable. We parse these
* first to permit arguments passed directly to the VM to override
@@ -892,6 +899,7 @@ namespace rubinius {
load_kernel(runtime);
start_signals();
+ start_finalizer();
run_file(runtime + "/loader.rbc");
state->vm()->thread_state()->clear();
View
@@ -13,6 +13,7 @@ namespace rubinius {
class ConfigParser;
class QueryAgent;
class SignalHandler;
+ class FinalizerHandler;
/**
* Thrown when unable to find Rubinius runtime directories.
@@ -56,7 +57,8 @@ namespace rubinius {
// The Ruby library version with which the .rbc file is compatible.
int version_;
- SignalHandler* sig_handler_;
+ SignalHandler* signal_handler_;
+ FinalizerHandler* finalizer_handler_;
std::string system_prefix_;
@@ -108,6 +110,7 @@ namespace rubinius {
int exit_code(STATE);
void start_signals();
+ void start_finalizer();
void start_agent(int port);
};
View
@@ -0,0 +1,171 @@
+#include "config.h"
+#include "vm.hpp"
+#include "finalizer.hpp"
+#include "on_stack.hpp"
+#include "objectmemory.hpp"
+
+#include "builtin/module.hpp"
+#include "builtin/array.hpp"
+
+#include "builtin/array.hpp"
+#include "builtin/module.hpp"
+#include "builtin/class.hpp"
+
+#include "capi/handle.hpp"
+
+#include "builtin/thread.hpp"
+
+namespace rubinius {
+
+ Object* finalizer_handler_tramp(STATE) {
+ state->shared().finalizer_handler()->perform(state);
+ return cNil;
+ }
+
+ FinalizerHandler::FinalizerHandler(STATE)
+ : AuxiliaryThread()
+ , shared_(state->shared())
+ , target_(state->vm())
+ , self_(NULL)
+ , thread_(state)
+ , exit_(false)
+ {
+ shared_.auxiliary_threads()->register_thread(this);
+ shared_.set_finalizer_handler(this);
+
+ lock_.init();
+ cond_.init();
+
+ start_thread(state);
+ }
+
+ FinalizerHandler::~FinalizerHandler() {
+ shared_.auxiliary_threads()->unregister_thread(this);
+ }
+
+ void FinalizerHandler::start_thread(STATE) {
+ SYNC(state);
+ if(self_) return;
+ self_ = state->shared().new_vm();
+ thread_.set(Thread::create(state, self_, G(thread), finalizer_handler_tramp, false, true));
+ run(state);
+ }
+
+ void FinalizerHandler::stop_thread(STATE) {
+ SYNC(state);
+ if(!self_) return;
+
+ // Thread might have already been stopped
+ pthread_t os = self_->os_thread();
+ exit_ = true;
+
+ cond_.signal();
+ void* return_value;
+ pthread_join(os, &return_value);
+ self_ = NULL;
+ }
+
+ void FinalizerHandler::shutdown(STATE) {
+ stop_thread(state);
+ }
+
+ void FinalizerHandler::before_exec(STATE) {
+ stop_thread(state);
+ }
+
+ void FinalizerHandler::after_exec(STATE) {
+ exit_ = false;
+ start_thread(state);
+ }
+
+ void FinalizerHandler::before_fork(STATE) {
+ stop_thread(state);
+ }
+
+ void FinalizerHandler::after_fork_parent(STATE) {
+ exit_ = false;
+ start_thread(state);
+ }
+
+ void FinalizerHandler::after_fork_child(STATE) {
+ exit_ = false;
+ cond_.init();
+ start_thread(state);
+ }
+
+ void FinalizerHandler::run(STATE) {
+ int error = thread_.get()->fork_attached(state);
+ if(error) rubinius::bug("Unable to start finalizer handler thread");
+ }
+
+ void FinalizerHandler::perform(STATE) {
+ GCTokenImpl gct;
+ utilities::thread::Thread::set_os_name("rbx.finalizer");
+
+ state->vm()->thread->hard_unlock(state, gct);
+
+ CallFrame* call_frame = 0;
+
+ while(!exit_) {
+ FinalizeObject* fi;
+
+ // Take the lock, remove the first one from the list,
+ // then process it.
+ {
+ utilities::thread::Mutex::LockGuard lg(lock_);
+
+ while(queue_.empty()) {
+ GCIndependent indy(state);
+ cond_.wait(lock_);
+ if(exit_) return;
+ }
+
+ fi = queue_.front();
+ queue_.pop_front();
+ }
+
+ state->vm()->set_call_frame(0);
+ if(fi->ruby_finalizer) {
+ // Rubinius specific code. If the finalizer is cTrue, then
+ // send the object the finalize message
+ if(fi->ruby_finalizer == cTrue) {
+ fi->object->send(state, call_frame, state->symbol("__finalize__"));
+ } else {
+ Array* ary = Array::create(state, 1);
+ ary->set(state, 0, fi->object->id(state));
+
+ OnStack<1> os(state, ary);
+
+ fi->ruby_finalizer->send(state, call_frame, G(sym_call), ary);
+ }
+ }
+
+ if(fi->finalizer) {
+ (*fi->finalizer)(state, fi->object);
+ }
+ // Unhook any handle used by fi->object so that we don't accidentally
+ // try and mark it later (after we've finalized it)
+ if(capi::Handle* handle = fi->object->handle(state)) {
+ handle->forget_object();
+ fi->object->clear_handle(state);
+ }
+
+ // If the object was remembered, unremember it.
+ if(fi->object->remembered_p()) {
+ state->memory()->unremember_object(fi->object);
+ }
+
+ fi->status = FinalizeObject::eFinalized;
+ }
+
+ }
+
+ void FinalizerHandler::schedule(FinalizeObject* fi) {
+ utilities::thread::Mutex::LockGuard lg(lock_);
+ queue_.push_back(fi);
+ }
+
+ void FinalizerHandler::signal() {
+ cond_.signal();
+ }
+}
View
@@ -0,0 +1,56 @@
+#ifndef RBX_FINALIZER_HPP
+#define RBX_FINALIZER_HPP
+
+#include "lock.hpp"
+#include "auxiliary_threads.hpp"
+
+#include "gc/finalize.hpp"
+#include "gc/root.hpp"
+
+#include <list>
+
+namespace rubinius {
+ class VM;
+ class State;
+ struct CallFrame;
+ class Thread;
+
+ Object* handle_tramp(STATE);
+
+ class FinalizerHandler : public AuxiliaryThread, public Lockable {
+ SharedState& shared_;
+ VM* target_;
+ VM* self_;
+
+ TypedRoot<Thread*> thread_;
+ std::list<FinalizeObject*> queue_;
+ utilities::thread::Mutex lock_;
+ utilities::thread::Condition cond_;
+ bool exit_;
+
+ public:
+
+ FinalizerHandler(STATE);
+ virtual ~FinalizerHandler();
+
+ void perform(State*);
+
+ void schedule(FinalizeObject* fi);
+ void signal();
+
+ void start_thread(STATE);
+ void stop_thread(STATE);
+
+ void shutdown(STATE);
+ void before_exec(STATE);
+ void after_exec(STATE);
+ void before_fork(STATE);
+ void after_fork_parent(STATE);
+ void after_fork_child(STATE);
+
+ void run(State*);
+ };
+}
+
+#endif
+
View
@@ -1,3 +1,6 @@
+#ifndef RBX_GC_FINALIZE_HPP
+#define RBX_GC_FINALIZE_HPP
+
namespace rubinius {
class Object;
@@ -31,3 +34,5 @@ namespace rubinius {
}
};
}
+
+#endif
Oops, something went wrong.

0 comments on commit f0a86ed

Please sign in to comment.