memory.cc
Go to the documentation of this file.
00001 #include "typelib.hh"
00002 #include <typelib/value_ops.hh>
00003 #include <ruby.h>
00004 #if !defined(RUBY_19) && !defined(RUBY_191)
00005 extern "C" {
00006 #include <st.h>
00007 }
00008 #endif
00009 
00010 using namespace Typelib;
00011 using namespace std;
00012 using namespace typelib_ruby;
00013 
00014 static VALUE cMemoryZone;
00015 static st_table* MemoryTable;
00016 
00017 /* For those who are wondering, st_data_t is always the size of an void*
00018  * (see the first lines of st.h
00019  */
00020 static int memory_table_compare(void* a, void* b)
00021 {
00022     return (a != b);
00023 }
00024 
00025 #if !defined(RUBY_19) && !defined(RUBY_191)
00026 typedef long st_index_t;
00027 #endif
00028 
00029 static st_index_t memory_table_hash(void* a)
00030 {
00031     /* Use the low-order bits as hash value, as they are the most likely to
00032      * change */
00033     return (st_index_t)a;
00034 }
00035 
00036 #if defined(RUBY_19)
00037 static struct st_hash_type memory_table_type = {
00038     (int (*)(...))memory_table_compare,
00039     (st_index_t (*)(...))memory_table_hash
00040 };
00041 #elif defined(RUBY_191)
00042 static struct st_hash_type memory_table_type = {
00043     (int (*)(...))memory_table_compare,
00044     (int (*)(...))memory_table_hash
00045 };
00046 #else
00047 static struct st_hash_type memory_table_type = {
00048     (int (*)())memory_table_compare,
00049     (int (*)())memory_table_hash
00050 };
00051 #endif
00052 
00053 struct MemoryTableEntry
00054 {
00055     int refcount;
00056     VALUE object;
00057     bool owned;
00058     void* root_ptr;
00059     MemoryTableEntry(VALUE object, bool owned, void* root_ptr = 0)
00060         : refcount(1), object(object), owned(owned), root_ptr(root_ptr)
00061     {
00062         if (root_ptr && owned)
00063             rb_raise(rb_eArgError, "given both a root pointer and owned=true for object %llu", NUM2ULL(rb_obj_id(object)));
00064     }
00065 };
00066 
00067 struct RbMemoryLayout
00068 {
00069     int refcount;
00070     MemoryLayout layout;
00071     boost::shared_ptr<Registry> registry;
00072 
00073     RbMemoryLayout()
00074         : refcount(0) {}
00075     RbMemoryLayout(MemoryLayout const& layout, boost::shared_ptr<Registry> registry)
00076         : refcount(0), layout(layout), registry(registry) {}
00077 };
00078 
00079 // MemoryTypes actually holds the memory layout of each memory zone. We cannot
00080 // simply use the Type object as, at exit, the Ruby GC do not order finalization
00081 // and therefore the registry can be deleted before the Type instances.
00082 typedef std::map< void const*, void const* > MemoryTypes;
00083 typedef std::map< void const*, RbMemoryLayout > TypeLayouts;
00084 MemoryTypes memory_types;
00085 TypeLayouts memory_layouts;
00086 
00087 static int
00088 memory_touch_i(volatile void* ptr, MemoryTableEntry* entry, st_data_t)
00089 {
00090     char value = *((char*)ptr);
00091     fprintf(stderr, "touching %p (refcount=%i)\n", ptr, entry->refcount);
00092     *((char*)ptr) = value;
00093     return ST_CONTINUE;
00094 }
00095 
00096 static void
00097 memory_touch_all()
00098 {
00099     st_foreach(MemoryTable, (int(*)(ANYARGS))memory_touch_i, (st_data_t)0);
00100 
00101 }
00102 
00103 void
00104 typelib_ruby::memory_unref(void *ptr)
00105 {
00106 #   ifdef VERBOSE
00107     fprintf(stderr, "%p: unref\n", ptr);
00108 #   endif
00109 
00110     // A NULL ptr can happen if the value has been invalidated. Just
00111     // ignore.
00112     if (!ptr)
00113         return;
00114 
00115     MemoryTableEntry* entry = 0;
00116     if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00117         rb_raise(rb_eArgError, "cannot find %p in memory table", ptr);
00118     --(entry->refcount);
00119     if (!entry->refcount)
00120     {
00121         if (entry->owned)
00122             memory_delete(ptr);
00123         if (entry->root_ptr)
00124             memory_unref(entry->root_ptr);
00125 
00126 #       ifdef VERBOSE
00127         fprintf(stderr, "%p: deregister\n", ptr);
00128 #       endif
00129         delete entry;
00130         st_delete(MemoryTable, (st_data_t*)&ptr, 0);
00131     }
00132 
00133     MemoryTypes::iterator type_it = memory_types.find(ptr);
00134     if (type_it != memory_types.end())
00135     {
00136         TypeLayouts::iterator layout_it = memory_layouts.find(type_it->second);
00137         RbMemoryLayout& layout = layout_it->second;
00138         if (0 == --layout.refcount)
00139             memory_layouts.erase(layout_it);
00140         memory_types.erase(type_it);
00141     }
00142 }
00143 
00144 void
00145 typelib_ruby::memory_delete(void *ptr)
00146 {
00147     MemoryTypes::iterator it = memory_types.find(ptr);
00148     if (it != memory_types.end())
00149     {
00150         TypeLayouts::iterator layout_it = memory_layouts.find(it->second);
00151         if (layout_it != memory_layouts.end())
00152         {
00153             RbMemoryLayout& layout = layout_it->second;
00154             Typelib::destroy(
00155                     static_cast<uint8_t*>(ptr),
00156                     layout.layout);
00157         }
00158     }
00159 
00160 #   ifdef VERBOSE
00161     fprintf(stderr, "%p: deallocated\n", ptr);
00162 #   endif
00163     free(ptr);
00164 }
00165 
00166 static VALUE
00167 memory_aref(void *ptr)
00168 {
00169     MemoryTableEntry* entry;
00170     if (!st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry)) {
00171         return Qnil;
00172     }
00173     if ((VALUE)entry == Qundef)
00174         rb_bug("found undef in memory table");
00175 
00176     return entry->object;
00177 }
00178 
00179 static void
00180 memory_aset(void *ptr, VALUE obj, bool owned, void* root_ptr)
00181 {
00182     if (! NIL_P(memory_aref(ptr)))
00183         rb_raise(rb_eArgError, "there is already a wrapper for %p", ptr);
00184 
00185     if (ptr == root_ptr)
00186         rb_raise(rb_eArgError, "pointer and root pointer are equal");
00187 
00188     MemoryTableEntry* entry = 0;
00189     if (st_lookup(MemoryTable, (st_data_t)ptr, (st_data_t*)&entry))
00190     {
00191         ++(entry->refcount);
00192     }
00193     else
00194     {
00195         entry = new MemoryTableEntry(obj, owned, root_ptr);
00196         st_insert(MemoryTable, (st_data_t)ptr, (st_data_t)entry);
00197 
00198         if (root_ptr)
00199         {
00200             MemoryTableEntry* root_entry = 0;
00201             if (!st_lookup(MemoryTable, (st_data_t)root_ptr, (st_data_t*)&root_entry))
00202                 rb_raise(rb_eArgError, "%p given as root pointer for %p but is not registered", root_ptr, ptr);
00203             ++(root_entry->refcount);
00204         }
00205     }
00206 }
00207 
00208 VALUE
00209 typelib_ruby::memory_allocate(size_t size)
00210 {
00211 #   ifdef VERBOSE
00212     memory_touch_all();
00213 #   endif
00214 
00215     void* ptr = malloc(size);
00216     VALUE zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_unref, ptr);
00217 #   ifdef VERBOSE
00218     fprintf(stderr, "%p: new allocated zone of size %lu\n", ptr, size);
00219 #   endif
00220     memory_aset(ptr, zone, true, 0);
00221     return zone;
00222 }
00223 
00224 void
00225 typelib_ruby::memory_init(VALUE ptr, VALUE type)
00226 {
00227     try {
00228         void* cptr = memory_cptr(ptr);
00229         MemoryTypes::iterator it = memory_types.find(cptr);
00230         if (it != memory_types.end())
00231             rb_raise(rb_eArgError, "memory zone already initialized");
00232 
00233         // For deinitialization later, get or register the type's layout
00234         Type const& t(rb2cxx::object<Type>(type));
00235         TypeLayouts::iterator layout_it = memory_layouts.find(&t);
00236         if (layout_it == memory_layouts.end())
00237         {
00238             cxx2rb::RbRegistry& registry = rb2cxx::object<cxx2rb::RbRegistry>(type_get_registry(type));
00239             layout_it = memory_layouts.insert(
00240                 make_pair( &t, RbMemoryLayout(layout_of(t, true), registry.registry) )
00241                 ).first;
00242         }
00243         RbMemoryLayout& layout = layout_it->second;
00244         ++layout.refcount;
00245 
00246         memory_types.insert( make_pair(cptr, &t) );
00247         Typelib::init(static_cast<uint8_t*>(cptr), layout.layout);
00248     } catch(std::exception const& e) {
00249         rb_raise(rb_eArgError, "internal error: %s", e.what());
00250     }
00251 }
00252 
00253 VALUE
00254 typelib_ruby::memory_wrap(void* ptr, bool take_ownership, void* root_ptr)
00255 {
00256     VALUE zone = memory_aref(ptr);
00257     if (NIL_P(zone))
00258     {
00259 #       ifdef VERBOSE
00260         fprintf(stderr, "%p: wrapping new memory zone\n", ptr);
00261 #       endif
00262 
00263         zone = Data_Wrap_Struct(cMemoryZone, 0, &memory_unref, ptr);
00264         memory_aset(ptr, zone, take_ownership, root_ptr);
00265     }
00266     else
00267     {
00268 #       ifdef VERBOSE
00269         fprintf(stderr, "%p: already known memory zone\n", ptr);
00270 #       endif
00271     }
00272 
00273     return zone;
00274 }
00275 
00276 void*
00277 typelib_ruby::memory_cptr(VALUE ptr)
00278 {
00279     void* cptr;
00280     Data_Get_Struct(ptr, void, cptr);
00281     return cptr;
00282 }
00283 
00284 static VALUE
00285 memory_zone_address(VALUE self)
00286 {
00287     void* ptr = memory_cptr(self);
00288     return ULL2NUM(reinterpret_cast<uint64_t>(ptr));
00289 }
00290 
00291 static VALUE
00292 memory_zone_to_ptr(VALUE self)
00293 {
00294 #   ifdef VERBOSE
00295     fprintf(stderr, "allocating void* to create a pointer-to-memory\n");
00296 #   endif
00297 
00298     VALUE result = memory_allocate(sizeof(void*));
00299 
00300     void* newptr = memory_cptr(result);
00301     void* ptr    = memory_cptr(self);
00302 
00303     *reinterpret_cast<void**>(newptr) = ptr;
00304     rb_iv_set(result, "@pointed_to_memory", self);
00305     return result;
00306 }
00307 
00308 static VALUE
00309 string_to_memory_ptr(VALUE self)
00310 {
00311     rb_str_modify(self);
00312 #   ifdef VERBOSE
00313     fprintf(stderr, "wrapping string value from Ruby\n");
00314 #   endif
00315     VALUE ptr = memory_wrap(StringValuePtr(self), false, 0);
00316     rb_iv_set(ptr, "@buffer_string", self);
00317     return ptr;
00318 }
00319 
00320 static VALUE
00321 memory_zone_table_size(VALUE self)
00322 {
00323     return INT2NUM(MemoryTable->num_entries);
00324 }
00325 
00326 void typelib_ruby::Typelib_init_memory()
00327 {
00328     VALUE mTypelib  = rb_define_module("Typelib");
00329     MemoryTable     = st_init_table(&memory_table_type);
00330     VALUE table     = Data_Wrap_Struct(rb_cObject, 0, 0, MemoryTable);
00331     rb_iv_set(mTypelib, "@__memory_table__", table);
00332 
00333     cMemoryZone = rb_define_class_under(mTypelib, "MemoryZone", rb_cObject);
00334     rb_define_method(cMemoryZone, "zone_address", RUBY_METHOD_FUNC(memory_zone_address), 0);
00335     rb_define_method(cMemoryZone, "to_ptr", RUBY_METHOD_FUNC(memory_zone_to_ptr), 0);
00336     rb_define_singleton_method(cMemoryZone, "table_size", RUBY_METHOD_FUNC(memory_zone_table_size), 0);
00337 
00338     rb_define_method(rb_cString, "to_memory_ptr", RUBY_METHOD_FUNC(string_to_memory_ptr), 0);
00339 }
00340 


typelib
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Thu Jan 2 2014 11:38:41