$search
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