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


typelib
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Mon Sep 14 2015 15:08:18