Skip to content

Instantly share code, notes, and snippets.

@brixen
Created July 20, 2009 16:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brixen/150437 to your computer and use it in GitHub Desktop.
Save brixen/150437 to your computer and use it in GitHub Desktop.
From 53a0ff5a8db607cba49dab1fb26e06ec82434f08 Mon Sep 17 00:00:00 2001
From: Brian Ford <bford@engineyard.com>
Date: Sun, 19 Jul 2009 10:00:09 -0700
Subject: [PATCH] Reworked capi cached handles management.
Handles are persistent references to managed-memory objects (i.e.
"Ruby" objects). Objects that have handles are kept alive either
by the handle having a greater than 0 reference count, or the object
being reachable from another object.
Further, there are essentially two types of handles: 1) those that
have cached data, and 2) those that do not. Handles can have cached
data when they support direct memory access. For example Strings and
Arrays accessed by RSTRING and RARRAY. This is provided purely for
compatibility with poorly written MRI C extensions.
Each NativeMethod call (i.e. a call to a C function in a C extension)
tracks which handles were requested during that call. The handle's
reference count is incremented when the handle is requested and
decremented when that native method call returns. Refernces to these
handles are kept in an STL Set class.
There is also a global list of handles maintained by the garbage
collector. This enables the GC to keep the handles is the list up-to-date
when the object it references is garbage collected.
When a regular handle is referenced in a way that creates cached data,
the handle is moved from the global list to a cached-data list. Both
lists are processed by the various GC stages. However, only the cached
data list is processed at native method call boundaries. When control
returns to Ruby-land from C-land, any cached data must be flushed back
to the Ruby objects so it is visible to Ruby code that manipulates them.
When control goes from Ruby-land to C-land, the cached data must be
updated from the Ruby objects so that C functions that operate on the
cached data will have the correct data.
---
vm/builtin/nativemethod.cpp | 71 +++++++++++++++++++++++-----------------
vm/builtin/nativemethod.hpp | 14 ++++----
vm/capi/array.cpp | 3 ++
vm/capi/capi.cpp | 2 +-
vm/capi/capi.hpp | 2 +-
vm/capi/data.cpp | 3 ++
vm/capi/float.cpp | 14 +++++---
vm/capi/handle.cpp | 35 +++++++++-----------
vm/capi/handle.hpp | 26 ++++++++++-----
vm/capi/kernel.cpp | 2 +-
vm/capi/numeric.cpp | 4 +-
vm/capi/object.cpp | 2 +-
vm/capi/string.cpp | 3 ++
vm/gc/baker.cpp | 8 +++++
vm/gc/gc.cpp | 5 +++
vm/gc/gc.hpp | 9 +++++-
vm/gc/immix.cpp | 4 ++
vm/linkedlist.hpp | 4 ++
vm/objectmemory.cpp | 9 +++--
vm/objectmemory.hpp | 2 +-
vm/shared_state.cpp | 2 +
vm/shared_state.hpp | 5 +++
vm/test/test_objectmemory.hpp | 3 +-
23 files changed, 150 insertions(+), 82 deletions(-)
diff --git a/vm/builtin/nativemethod.cpp b/vm/builtin/nativemethod.cpp
index aff2e97..5ce973d 100644
--- a/vm/builtin/nativemethod.cpp
+++ b/vm/builtin/nativemethod.cpp
@@ -34,8 +34,8 @@ namespace rubinius {
}
NativeMethodFrame::~NativeMethodFrame() {
- flush_cached_data(true);
- for(capi::HandleList::iterator i = handles_.begin();
+ flush_cached_data();
+ for(capi::HandleSet::iterator i = handles_.begin();
i != handles_.end();
i++) {
capi::Handle* handle = *i;
@@ -58,8 +58,8 @@ namespace rubinius {
}
handle->ref();
+ handles_.insert(handle);
- handles_.push_back(handle);
return handle->as_value();
}
@@ -82,37 +82,48 @@ namespace rubinius {
return *data_;
}
- void NativeMethodFrame::flush_cached_data(bool release_memory) {
+ void NativeMethodFrame::flush_cached_data() {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
-
- for(capi::HandleList::iterator i = handles_.begin();
- i != handles_.end();
- i++) {
- capi::Handle* handle = *i;
- if(handle->is_rarray()) {
- capi::capi_get_array(env, handle->as_value());
- } else if(handle->is_rstring()) {
- capi::capi_get_string(env, handle->as_value());
- } else if(handle->is_rdata()) {
- capi::capi_rdata_flush_handle(env, handle);
- } else if(handle->is_rfloat()) {
- capi::capi_get_float(handle->as_value());
+ capi::Handles* handles = env->state()->shared.cached_handles();
+
+ if(handles->size() > 0) {
+ for(capi::Handles::Iterator i(*handles); i.more(); i.advance()) {
+ switch(i->type()) {
+ case capi::cRArray:
+ capi::capi_get_array(env, i->as_value());
+ break;
+ case capi::cRString:
+ capi::capi_get_string(env, i->as_value());
+ break;
+ case capi::cRData:
+ capi::capi_rdata_flush_handle(env, i.current());
+ break;
+ case capi::cRFloat:
+ capi::capi_get_float(env, i->as_value());
+ break;
+ default:
+ break;
+ }
}
-
- if(release_memory) handle->free_data();
}
}
void NativeMethodFrame::update_cached_data() {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
- for(capi::HandleList::iterator i = handles_.begin();
- i != handles_.end();
- i++) {
- capi::Handle* handle = *i;
- if(handle->is_rarray()) {
- capi::capi_update_array(env, handle->as_value());
- } else if(handle->is_rstring()) {
- capi::capi_update_string(env, handle->as_value());
+ capi::Handles* handles = env->state()->shared.cached_handles();
+
+ if(handles->size() > 0) {
+ for(capi::Handles::Iterator i(*handles); i.more(); i.advance()) {
+ switch(i->type()) {
+ case capi::cRArray:
+ capi::capi_update_array(env, i->as_value());
+ break;
+ case capi::cRString:
+ capi::capi_update_string(env, i->as_value());
+ break;
+ default:
+ break;
+ }
}
}
}
@@ -169,7 +180,7 @@ namespace rubinius {
return current_block_.get();
}
- capi::HandleList& NativeMethodEnvironment::handles() {
+ capi::HandleSet& NativeMethodEnvironment::handles() {
return current_native_frame_->handles();
}
@@ -185,8 +196,8 @@ namespace rubinius {
return current_native_frame_->data();
}
- void NativeMethodEnvironment::flush_cached_data(bool release_memory) {
- current_native_frame_->flush_cached_data(release_memory);
+ void NativeMethodEnvironment::flush_cached_data() {
+ current_native_frame_->flush_cached_data();
}
void NativeMethodEnvironment::update_cached_data() {
diff --git a/vm/builtin/nativemethod.hpp b/vm/builtin/nativemethod.hpp
index 2ed7077..98b5576 100644
--- a/vm/builtin/nativemethod.hpp
+++ b/vm/builtin/nativemethod.hpp
@@ -110,7 +110,7 @@ namespace rubinius {
}
/** Set of Handles available in current Frame (convenience.) */
- capi::HandleList& handles();
+ capi::HandleSet& handles();
/** Returns the map of RSTRING structs in the current NativeMethodFrame. */
CApiStructs& strings();
@@ -122,7 +122,7 @@ namespace rubinius {
CApiStructs& arrays();
/** Flush RARRAY, RSTRING, etc. caches, possibly releasing memory. */
- void flush_cached_data(bool release_memory);
+ void flush_cached_data();
/** Updates cached data with changes to the Ruby objects. */
void update_cached_data();
@@ -135,8 +135,8 @@ namespace rubinius {
class NativeMethodFrame {
/** Native Frame active before this call. @note This may NOT be the sender. --rue */
NativeMethodFrame* previous_;
- /** HandleList to Objects used in this Frame. */
- capi::HandleList handles_;
+ /** HandleSet to Objects used in this Frame. */
+ capi::HandleSet handles_;
/** RARRAY structs allocated during this call. */
CApiStructs* arrays_;
/** RDATA structs allocated during this call. */
@@ -168,15 +168,15 @@ namespace rubinius {
Object* get_object(VALUE hndl);
/** Flush RARRAY, RSTRING, etc. caches, possibly releasing memory. */
- void flush_cached_data(bool release_memory);
+ void flush_cached_data();
/** Updates cached data with changes to the Ruby objects. */
void update_cached_data();
public: /* Accessors */
- /** HandleList to Objects used in this Frame. */
- capi::HandleList& handles() {
+ /** HandleSet to Objects used in this Frame. */
+ capi::HandleSet& handles() {
return handles_;
}
diff --git a/vm/capi/array.cpp b/vm/capi/array.cpp
index 2b89300..d129594 100644
--- a/vm/capi/array.cpp
+++ b/vm/capi/array.cpp
@@ -113,6 +113,9 @@ namespace rubinius {
type_ = cRArray;
as_.rarray = ary;
+
+ env->state()->shared.global_handles()->move(this,
+ env->state()->shared.cached_handles());
}
return as_.rarray;
diff --git a/vm/capi/capi.cpp b/vm/capi/capi.cpp
index 3e7b451..9878fad 100644
--- a/vm/capi/capi.cpp
+++ b/vm/capi/capi.cpp
@@ -118,7 +118,7 @@ namespace rubinius {
VALUE capi_funcall_backend(const char* file, int line,
VALUE receiver, ID method_name, std::size_t arg_count, VALUE* arg_array) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
- env->flush_cached_data(false);
+ env->flush_cached_data();
Array* args = Array::create(env->state(), arg_count);
for(size_t i = 0; i < arg_count; i++) {
diff --git a/vm/capi/capi.hpp b/vm/capi/capi.hpp
index a19e1a3..d7ae4dc 100644
--- a/vm/capi/capi.hpp
+++ b/vm/capi/capi.hpp
@@ -152,7 +152,7 @@ namespace rubinius {
/** Get a Float object for a handle ensuring that RFLOAT data has
* been flushed. */
- Float* capi_get_float(VALUE float_handle);
+ Float* capi_get_float(NativeMethodEnvironment* env, VALUE float_handle);
/** Converts a native type (int, uint, long) to a suitable Integer. */
template<typename NativeType>
diff --git a/vm/capi/data.cpp b/vm/capi/data.cpp
index 0347be9..676c19e 100644
--- a/vm/capi/data.cpp
+++ b/vm/capi/data.cpp
@@ -56,6 +56,9 @@ namespace rubinius {
type_ = cRData;
as_.rdata = d;
+
+ env->state()->shared.global_handles()->move(this,
+ env->state()->shared.cached_handles());
}
return as_.rdata;
diff --git a/vm/capi/float.cpp b/vm/capi/float.cpp
index 27632b3..4f0faf5 100644
--- a/vm/capi/float.cpp
+++ b/vm/capi/float.cpp
@@ -9,19 +9,19 @@ using namespace rubinius::capi;
namespace rubinius {
namespace capi {
- Float* capi_get_float(VALUE float_handle) {
+ Float* capi_get_float(NativeMethodEnvironment* env, VALUE float_handle) {
Handle* handle = Handle::from(float_handle);
Float* float_obj = c_as<Float>(handle->object());
if(handle->is_rfloat()) {
// Flushing value from the RFloat back to the Float object
- RFloat* f = handle->as_rfloat();
+ RFloat* f = handle->as_rfloat(env);
float_obj->val = f->value;
}
return float_obj;
}
- RFloat* Handle::as_rfloat() {
+ RFloat* Handle::as_rfloat(NativeMethodEnvironment* env) {
if(type_ != cRFloat) {
Float* float_obj = c_as<Float>(object());
@@ -30,6 +30,9 @@ namespace rubinius {
type_ = cRFloat;
as_.rfloat = f;
+
+ env->state()->shared.global_handles()->move(this,
+ env->state()->shared.cached_handles());
}
return as_.rfloat;
@@ -39,8 +42,9 @@ namespace rubinius {
extern "C" {
struct RFloat* capi_rfloat_struct(VALUE float_handle) {
- Handle* handle = Handle::from(float_handle);
- return handle->as_rfloat();
+ NativeMethodEnvironment* env = NativeMethodEnvironment::get();
+
+ return Handle::from(float_handle)->as_rfloat(env);
}
VALUE rb_float_new(double val) {
diff --git a/vm/capi/handle.cpp b/vm/capi/handle.cpp
index 45743bf..7dca1aa 100644
--- a/vm/capi/handle.cpp
+++ b/vm/capi/handle.cpp
@@ -6,30 +6,26 @@
namespace rubinius {
namespace capi {
void Handle::free_data() {
- switch(type_) {
- case cRArray:
- if(as_.rarray) {
+ if(as_.cache_data) {
+ switch(type_) {
+ case cRArray:
delete[] as_.rarray->dmwmb;
delete as_.rarray;
- }
-
- as_.rarray = 0;
- break;
- case cRData:
- if(as_.rdata) delete as_.rdata;
- as_.rdata = 0;
- case cRString:
- if(as_.rstring) {
+ break;
+ case cRData:
+ delete as_.rdata;
+ break;
+ case cRString:
delete[] as_.rstring->dmwmb;
delete as_.rstring;
+ break;
+ case cRFloat:
+ delete as_.rfloat;
+ break;
+ default:
+ break;
}
-
- as_.rstring = 0;
- case cRFloat:
- if(as_.rfloat) delete as_.rfloat;
- as_.rfloat = 0;
- default:
- break;
+ as_.cache_data = 0;
}
type_ = cUnknown;
@@ -45,6 +41,7 @@ namespace rubinius {
}
Handle::~Handle() {
+ free_data();
invalidate();
}
}
diff --git a/vm/capi/handle.hpp b/vm/capi/handle.hpp
index 654ac3b..ca5e73c 100644
--- a/vm/capi/handle.hpp
+++ b/vm/capi/handle.hpp
@@ -6,7 +6,7 @@
#include "capi/value.hpp"
-#include <list>
+#include <tr1/unordered_set>
#include <vector>
struct RArray;
@@ -34,10 +34,11 @@ namespace rubinius {
unsigned int checksum_;
union {
- RArray* rarray;
- RString* rstring;
- RData* rdata;
- RFloat* rfloat;
+ RArray* rarray;
+ RString* rstring;
+ RData* rdata;
+ RFloat* rfloat;
+ intptr_t cache_data;
} as_;
public:
@@ -48,7 +49,7 @@ namespace rubinius {
, references_(0)
, checksum_(0xffff)
{
- as_.rarray = 0;
+ as_.cache_data = 0;
}
~Handle();
@@ -109,10 +110,14 @@ namespace rubinius {
return type_ == cRFloat;
}
+ HandleType type() {
+ return type_;
+ }
+
RArray* as_rarray(NativeMethodEnvironment* env);
RData* as_rdata(NativeMethodEnvironment* env);
RString* as_rstring(NativeMethodEnvironment* env);
- RFloat* as_rfloat();
+ RFloat* as_rfloat(NativeMethodEnvironment* env);
void free_data();
};
@@ -123,11 +128,16 @@ namespace rubinius {
return static_cast<Handle*>(head());
}
+ void move(Node* node, Handles* handles) {
+ remove(node);
+ handles->add(node);
+ }
+
typedef LinkedList::Iterator<Handles, Handle> Iterator;
};
- typedef std::list<Handle*> HandleList;
+ typedef std::tr1::unordered_set<Handle*> HandleSet;
}
}
diff --git a/vm/capi/kernel.cpp b/vm/capi/kernel.cpp
index 1e45e9a..4a18b12 100644
--- a/vm/capi/kernel.cpp
+++ b/vm/capi/kernel.cpp
@@ -28,7 +28,7 @@ extern "C" {
VALUE rb_apply(VALUE recv, ID mid, VALUE args) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
- env->flush_cached_data(false);
+ env->flush_cached_data();
Array* ary = capi::c_as<Array>(env->get_object(args));
diff --git a/vm/capi/numeric.cpp b/vm/capi/numeric.cpp
index e0357ff..519c296 100644
--- a/vm/capi/numeric.cpp
+++ b/vm/capi/numeric.cpp
@@ -24,7 +24,7 @@ extern "C" {
} else if(Bignum* big = try_as<Bignum>(object)) {
return big->to_long();
} else if(try_as<Float>(object)) {
- return (long)capi_get_float(obj)->val;
+ return (long)capi_get_float(env, obj)->val;
}
obj = rb_funcall(obj, rb_intern("to_int"), 0);
@@ -108,7 +108,7 @@ extern "C" {
double rb_num2dbl(VALUE val) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
if(try_as<Float>(env->get_object(val))) {
- return capi_get_float(val)->val;
+ return capi_get_float(env, val)->val;
}
// @todo should coerce other types
diff --git a/vm/capi/object.cpp b/vm/capi/object.cpp
index dd5309b..16e0e0c 100644
--- a/vm/capi/object.cpp
+++ b/vm/capi/object.cpp
@@ -190,7 +190,7 @@ extern "C" {
VALUE rb_obj_clone(VALUE obj_handle) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
- env->flush_cached_data(false);
+ env->flush_cached_data();
return rb_funcall(obj_handle, rb_intern("clone"), 0);
}
diff --git a/vm/capi/string.cpp b/vm/capi/string.cpp
index e400d3b..f67a3a3 100644
--- a/vm/capi/string.cpp
+++ b/vm/capi/string.cpp
@@ -106,6 +106,9 @@ namespace rubinius {
type_ = cRString;
as_.rstring = str;
+
+ env->state()->shared.global_handles()->move(this,
+ env->state()->shared.cached_handles());
}
return as_.rstring;
diff --git a/vm/gc/baker.cpp b/vm/gc/baker.cpp
index b8ed841..c982ab0 100644
--- a/vm/gc/baker.cpp
+++ b/vm/gc/baker.cpp
@@ -135,6 +135,14 @@ namespace rubinius {
assert(i->object()->type_id() != InvalidType);
}
+ for(capi::Handles::Iterator i(*data.cached_handles()); i.more(); i.advance()) {
+ if(!i->weak_p() && i->object()->young_object_p()) {
+ i->set_object(saw_object(i->object()));
+ }
+
+ assert(i->object()->type_id() != InvalidType);
+ }
+
for(VariableRootBuffers::Iterator i(data.variable_buffers());
i.more(); i.advance()) {
Object*** buffer = i->buffer();
diff --git a/vm/gc/gc.cpp b/vm/gc/gc.cpp
index 8adced0..b7142cd 100644
--- a/vm/gc/gc.cpp
+++ b/vm/gc/gc.cpp
@@ -23,6 +23,7 @@ namespace rubinius {
, call_frames_(state->shared.call_frame_locations())
, variable_buffers_(*state->variable_buffers())
, handles_(state->shared.global_handles())
+ , cached_handles_(state->shared.cached_handles())
, global_cache_(state->shared.global_cache)
{}
@@ -295,6 +296,10 @@ namespace rubinius {
visit.call(i->object());
}
+ for(capi::Handles::Iterator i(*data.cached_handles()); i.more(); i.advance()) {
+ visit.call(i->object());
+ }
+
visit.drain_stack();
}
diff --git a/vm/gc/gc.hpp b/vm/gc/gc.hpp
index 735ea10..4b5e5e9 100644
--- a/vm/gc/gc.hpp
+++ b/vm/gc/gc.hpp
@@ -29,16 +29,19 @@ namespace rubinius {
CallFrameLocationList& call_frames_;
VariableRootBuffers& variable_buffers_;
capi::Handles* handles_;
+ capi::Handles* cached_handles_;
GlobalCache* global_cache_;
public:
GCData(STATE);
GCData(Roots& r, CallFrameLocationList& l, VariableRootBuffers& b,
- capi::Handles* handles = NULL, GlobalCache *cache = NULL)
+ capi::Handles* handles = NULL, capi::Handles* cached_handles = NULL,
+ GlobalCache *cache = NULL)
: roots_(r)
, call_frames_(l)
, variable_buffers_(b)
, handles_(handles)
+ , cached_handles_(cached_handles)
, global_cache_(cache)
{}
@@ -58,6 +61,10 @@ namespace rubinius {
return handles_;
}
+ capi::Handles* cached_handles() {
+ return cached_handles_;
+ }
+
GlobalCache* global_cache() {
return global_cache_;
}
diff --git a/vm/gc/immix.cpp b/vm/gc/immix.cpp
index 4d60758..6b93214 100644
--- a/vm/gc/immix.cpp
+++ b/vm/gc/immix.cpp
@@ -112,6 +112,10 @@ namespace rubinius {
if(!i->weak_p()) saw_object(i->object());
}
+ for(capi::Handles::Iterator i(*data.cached_handles()); i.more(); i.advance()) {
+ if(!i->weak_p()) saw_object(i->object());
+ }
+
for(VariableRootBuffers::Iterator i(data.variable_buffers());
i.more(); i.advance()) {
Object*** buffer = i->buffer();
diff --git a/vm/linkedlist.hpp b/vm/linkedlist.hpp
index d52a813..e1ed6a9 100644
--- a/vm/linkedlist.hpp
+++ b/vm/linkedlist.hpp
@@ -85,6 +85,10 @@ public:
return current_;
}
+ Root* current() {
+ return current_;
+ }
+
Root* next() {
Root* ret = current_;
if(current_) {
diff --git a/vm/objectmemory.cpp b/vm/objectmemory.cpp
index 9881b21..a0ac66a 100644
--- a/vm/objectmemory.cpp
+++ b/vm/objectmemory.cpp
@@ -108,7 +108,8 @@ namespace rubinius {
void ObjectMemory::collect_young(GCData& data) {
static int collect_times = 0;
young.collect(data);
- prune_handles(true);
+ prune_handles(data.handles(), true);
+ prune_handles(data.cached_handles(), true);
collect_times++;
data.global_cache()->prune_young();
@@ -125,7 +126,8 @@ namespace rubinius {
mark_sweep_.after_marked();
immix_.clean_weakrefs();
- prune_handles(false);
+ prune_handles(data.handles(), false);
+ prune_handles(data.cached_handles(), false);
data.global_cache()->prune_unmarked();
@@ -137,8 +139,7 @@ namespace rubinius {
#endif
}
- void ObjectMemory::prune_handles(bool check_forwards) {
- capi::Handles* handles = state->shared.global_handles();
+ void ObjectMemory::prune_handles(capi::Handles* handles, bool check_forwards) {
capi::Handle* handle = handles->front();
capi::Handle* current;
diff --git a/vm/objectmemory.hpp b/vm/objectmemory.hpp
index c9506ad..b6ecaa1 100644
--- a/vm/objectmemory.hpp
+++ b/vm/objectmemory.hpp
@@ -100,7 +100,7 @@ namespace rubinius {
void debug_marksweep(bool val);
void add_type_info(TypeInfo* ti);
- void prune_handles(bool check_forwards);
+ void prune_handles(capi::Handles* handles, bool check_forwards);
ObjectPosition validate_object(Object* obj);
diff --git a/vm/shared_state.cpp b/vm/shared_state.cpp
index b338062..c8f336c 100644
--- a/vm/shared_state.cpp
+++ b/vm/shared_state.cpp
@@ -105,6 +105,7 @@ namespace rubinius {
: initialized_(false)
, signal_handler_(0)
, global_handles_(new capi::Handles)
+ , cached_handles_(new capi::Handles)
, profiling_(false)
, profiler_collection_(0)
, global_serial_(0)
@@ -127,6 +128,7 @@ namespace rubinius {
delete om;
delete global_cache;
delete global_handles_;
+ delete cached_handles_;
}
void SharedState::discard(SharedState* ss) {
diff --git a/vm/shared_state.hpp b/vm/shared_state.hpp
index ea75f67..f6f2dbd 100644
--- a/vm/shared_state.hpp
+++ b/vm/shared_state.hpp
@@ -60,6 +60,7 @@ namespace rubinius {
CallFrameLocationList cf_locations_;
VariableRootBuffers root_buffers_;
capi::Handles* global_handles_;
+ capi::Handles* cached_handles_;
bool profiling_;
profiler::ProfilerCollection* profiler_collection_;
int global_serial_;
@@ -115,6 +116,10 @@ namespace rubinius {
return global_handles_;
}
+ capi::Handles* cached_handles() {
+ return cached_handles_;
+ }
+
bool profiling() {
return profiling_;
}
diff --git a/vm/test/test_objectmemory.hpp b/vm/test/test_objectmemory.hpp
index f688d9f..9481bb7 100644
--- a/vm/test/test_objectmemory.hpp
+++ b/vm/test/test_objectmemory.hpp
@@ -20,12 +20,13 @@ public:
CallFrameLocationList call_frames;
VariableRootBuffers variable_buffers;
capi::Handles handles;
+ capi::Handles cached_handles;
void setUp() {
create();
roots = &state->globals.roots;
gc_data = new GCData(*roots, call_frames, variable_buffers,
- &handles, state->global_cache);
+ &handles, &cached_handles, state->global_cache);
}
void tearDown() {
--
1.6.1.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment