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


typelib
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Sat Jun 8 2019 18:49:22