From: Fedor Yudanov Date: Tue, 9 Apr 2013 09:53:14 +0000 (+0700) Subject: #50 - more memory control X-Git-Tag: v1.2.12~296^2~21 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ebab4a956bfee136b072ea087bf09421beb6fc23;p=platform%2Fupstream%2Fejdb.git #50 - more memory control --- diff --git a/rbejdb/src/rbbson.c b/rbejdb/src/rbbson.c index 79de787..013df03 100644 --- a/rbejdb/src/rbbson.c +++ b/rbejdb/src/rbbson.c @@ -5,6 +5,7 @@ #include #include +#define BSON_CONTEXT_RUBY_CLASS "EJDB_BSON_CONTEXT" #define BSON_RUBY_CLASS "EJDB_BSON" typedef struct { @@ -15,10 +16,14 @@ typedef struct { int flags; VALUE traverse_hash; +} RBBSON_CONTEXT; + +typedef struct { + bson* bsonval; } RBBSON; -VALUE iterate_array_callback(VALUE val, VALUE bsonWrap); +VALUE iterate_array_callback(VALUE val, VALUE bsonContextWrap); VALUE bson_array_to_ruby(bson_iterator* it); @@ -29,27 +34,41 @@ void ruby_to_bson_internal(VALUE rbobj, bson** bsonresp, VALUE traverse, int fla VALUE bson_date_to_ruby(bson_date_t date); +VALUE bsonContextClass = Qnil; VALUE bsonWrapClass = Qnil; void init_ruby_to_bson() { + bsonContextClass = rb_define_class(BSON_CONTEXT_RUBY_CLASS, rb_cObject); bsonWrapClass = rb_define_class(BSON_RUBY_CLASS, rb_cObject); } -VALUE createBsonWrap(bson* bsonval, VALUE rbobj, VALUE traverse, int flags) { +VALUE createBsonContextWrap(bson* bsonval, VALUE rbobj, VALUE traverse, int flags) { + if (NIL_P(bsonContextClass)) { + rb_raise(rb_eRuntimeError, "Ruby to BSON library must be initialized"); + } + VALUE bsonContextWrap = Data_Wrap_Struct(bsonContextClass, NULL, NULL, ruby_xmalloc(sizeof(RBBSON_CONTEXT))); + + RBBSON_CONTEXT* rbbsctx; + Data_Get_Struct(bsonContextWrap, RBBSON_CONTEXT, rbbsctx); + rbbsctx->bsonval = bsonval; + rbbsctx->obj = rbobj; + rbbsctx->arrayIndex = 0; + rbbsctx->traverse_hash = !NIL_P(traverse) ? traverse : rb_hash_new(); + + return bsonContextWrap; +} + +VALUE createBsonWrap(bson* bsonval) { if (NIL_P(bsonWrapClass)) { rb_raise(rb_eRuntimeError, "Ruby to BSON library must be initialized"); } VALUE bsonWrap = Data_Wrap_Struct(bsonWrapClass, NULL, NULL, ruby_xmalloc(sizeof(RBBSON))); - RBBSON* rbbson; Data_Get_Struct(bsonWrap, RBBSON, rbbson); - rbbson->bsonval = bsonval; - rbbson->obj = rbobj; - rbbson->arrayIndex = 0; - rbbson->traverse_hash = !NIL_P(traverse) ? traverse : rb_hash_new(); + rbbson->bsonval = bsonval; return bsonWrap; } @@ -63,15 +82,15 @@ void add_ruby_to_traverse(VALUE rbobj, VALUE traverse) { } -int iterate_key_values_callback(VALUE key, VALUE val, VALUE bsonWrap) { +int iterate_key_values_callback(VALUE key, VALUE val, VALUE bsonContextWrap) { key = rb_funcall(key, rb_intern("to_s"), 0); char* attrName = StringValuePtr(key); if (attrName[0] == '@') attrName++; // hack, removing @ - RBBSON* rbbson; - Data_Get_Struct(bsonWrap, RBBSON, rbbson); - bson* b = rbbson->bsonval; + RBBSON_CONTEXT* rbbsctx; + Data_Get_Struct(bsonContextWrap, RBBSON_CONTEXT, rbbsctx); + bson* b = rbbsctx->bsonval; switch (TYPE(val)) { case T_OBJECT: @@ -96,14 +115,14 @@ int iterate_key_values_callback(VALUE key, VALUE val, VALUE bsonWrap) { //else same as hash :) case T_HASH: { bson* subbson; - ruby_to_bson_internal(val, &subbson, rbbson->traverse_hash, rbbson->flags); + ruby_to_bson_internal(val, &subbson, rbbsctx->traverse_hash, rbbsctx->flags); bson_append_bson(b, attrName, subbson); } break; case T_ARRAY: - add_ruby_to_traverse(val, rbbson->traverse_hash); + add_ruby_to_traverse(val, rbbsctx->traverse_hash); bson_append_start_array(b, attrName); - rb_iterate(rb_each, val, iterate_array_callback, createBsonWrap(b, rbbson->obj, rbbson->traverse_hash, rbbson->flags)); + rb_iterate(rb_each, val, iterate_array_callback, createBsonContextWrap(b, rbbsctx->obj, rbbsctx->traverse_hash, rbbsctx->flags)); bson_append_finish_array(b); break; case T_STRING: @@ -150,20 +169,20 @@ int iterate_key_values_callback(VALUE key, VALUE val, VALUE bsonWrap) { return 0; } -VALUE iterate_array_callback(VALUE val, VALUE bsonWrap) { - RBBSON* rbbson; - Data_Get_Struct(bsonWrap, RBBSON, rbbson); +VALUE iterate_array_callback(VALUE val, VALUE bsonContextWrap) { + RBBSON_CONTEXT* rbbsctx; + Data_Get_Struct(bsonContextWrap, RBBSON_CONTEXT, rbbsctx); - iterate_key_values_callback(INT2NUM(rbbson->arrayIndex++), val, bsonWrap); + iterate_key_values_callback(INT2NUM(rbbsctx->arrayIndex++), val, bsonContextWrap); return val; } -VALUE iterate_object_attrs_callback(VALUE key, VALUE bsonWrap) { - RBBSON* rbbson; - Data_Get_Struct(bsonWrap, RBBSON, rbbson); - VALUE val = rb_funcall(rbbson->obj, rb_intern("instance_variable_get"), 1, key); +VALUE iterate_object_attrs_callback(VALUE key, VALUE bsonContextWrap) { + RBBSON_CONTEXT* rbbsctx; + Data_Get_Struct(bsonContextWrap, RBBSON_CONTEXT, rbbsctx); + VALUE val = rb_funcall(rbbsctx->obj, rb_intern("instance_variable_get"), 1, key); - iterate_key_values_callback(key, val, bsonWrap); + iterate_key_values_callback(key, val, bsonContextWrap); return val; } @@ -174,41 +193,41 @@ bson_date_t ruby_time_to_bson_internal(VALUE time) { } -void ruby_object_to_bson_internal(VALUE rbobj, VALUE bsonWrap) { +void ruby_object_to_bson_internal(VALUE rbobj, VALUE bsonContextWrap) { Check_Type(rbobj, T_OBJECT); VALUE attrs = rb_funcall(rbobj, rb_intern("instance_variables"), 0); Check_Type(attrs, T_ARRAY); - rb_iterate(rb_each, attrs, iterate_object_attrs_callback, bsonWrap); + rb_iterate(rb_each, attrs, iterate_object_attrs_callback, bsonContextWrap); } -void ruby_hash_to_bson_internal(VALUE rbhash, VALUE bsonWrap) { +void ruby_hash_to_bson_internal(VALUE rbhash, VALUE bsonContextWrap) { Check_Type(rbhash, T_HASH); - rb_hash_foreach(rbhash, iterate_key_values_callback, bsonWrap); + rb_hash_foreach(rbhash, iterate_key_values_callback, bsonContextWrap); } void ruby_to_bson_internal(VALUE rbobj, bson** bsonresp, VALUE traverse, int flags) { - VALUE bsonWrap = createBsonWrap(bson_create(), rbobj, traverse, flags); - RBBSON* rbbson; - Data_Get_Struct(bsonWrap, RBBSON, rbbson); + VALUE bsonContextWrap = createBsonContextWrap(bson_create(), rbobj, traverse, flags); + RBBSON_CONTEXT* rbbsctx; + Data_Get_Struct(bsonContextWrap, RBBSON_CONTEXT, rbbsctx); - add_ruby_to_traverse(rbobj, rbbson->traverse_hash); + add_ruby_to_traverse(rbobj, rbbsctx->traverse_hash); if (flags & RUBY_TO_BSON_AS_QUERY) { - bson_init_as_query(rbbson->bsonval); + bson_init_as_query(rbbsctx->bsonval); } else { - bson_init(rbbson->bsonval); + bson_init(rbbsctx->bsonval); } switch (TYPE(rbobj)) { case T_OBJECT: case T_DATA: - ruby_object_to_bson_internal(rbobj, bsonWrap); + ruby_object_to_bson_internal(rbobj, bsonContextWrap); break; case T_HASH: - ruby_hash_to_bson_internal(rbobj, bsonWrap); + ruby_hash_to_bson_internal(rbobj, bsonContextWrap); break; default: { VALUE objStr = rb_funcall(rbobj, rb_intern("inspect"), 0); @@ -216,9 +235,9 @@ void ruby_to_bson_internal(VALUE rbobj, bson** bsonresp, VALUE traverse, int fla } } - bson_finish(rbbson->bsonval); + bson_finish(rbbsctx->bsonval); - *bsonresp = rbbson->bsonval; + *bsonresp = rbbsctx->bsonval; } void ruby_to_bson(VALUE rbobj, bson** bsonresp, int flags) { @@ -313,6 +332,26 @@ VALUE bson_to_ruby(const bson* bsonval) { return res; } +VALUE bson_to_ruby_wrapper(VALUE bsonWrap) { + RBBSON* rbbson; + Data_Get_Struct(bsonWrap, RBBSON, rbbson); + return bson_to_ruby(rbbson->bsonval); +} + +VALUE bson_to_ruby_ensure_destroy_wrapper(VALUE bsonWrap, VALUE exception) { + RBBSON* rbbson; + Data_Get_Struct(bsonWrap, RBBSON, rbbson); + if (rbbson->bsonval) { + bson_destroy(rbbson->bsonval); + } + rbbson->bsonval = NULL; +} + +VALUE bson_to_ruby_ensure_destroy(bson* bsonval) { + VALUE bsonWrap = createBsonWrap(bsonval); + return rb_ensure(bson_to_ruby_wrapper, bsonWrap, bson_to_ruby_ensure_destroy_wrapper, bsonWrap); +} + VALUE bson_oid_to_ruby(const bson_oid_t* oid) { char oidhex[25]; bson_oid_to_string(oid, oidhex); diff --git a/rbejdb/src/rbbson.h b/rbejdb/src/rbbson.h index 0de6b32..e7af5d9 100644 --- a/rbejdb/src/rbbson.h +++ b/rbejdb/src/rbbson.h @@ -23,6 +23,9 @@ extern "C" { VALUE bson_to_ruby(const bson* bsonval); + VALUE bson_to_ruby_ensure_destroy(bson* bsonval); + + VALUE bson_oid_to_ruby(const bson_oid_t* oid); bson_oid_t ruby_to_bson_oid(VALUE rboid); diff --git a/rbejdb/src/rbejdb.c b/rbejdb/src/rbejdb.c index edbf60a..dac6da4 100644 --- a/rbejdb/src/rbejdb.c +++ b/rbejdb/src/rbejdb.c @@ -80,7 +80,6 @@ static int nil_or_raise_ejdb_error(EJDB *ejdb) { } - EJDB* getEJDB(VALUE self) { RBEJDB* rejdb; Data_Get_Struct(self, RBEJDB, rejdb); @@ -89,7 +88,7 @@ EJDB* getEJDB(VALUE self) { VALUE EJDB_new(VALUE self) { - rb_raise(rb_eRuntimeError, "EJDB.open method should be used!"); + rb_raise(rb_eRuntimeError, "EJDB.open() method should be used!"); return self; } @@ -129,8 +128,7 @@ VALUE EJDB_is_open(VALUE self) { } void EJDB_close(VALUE self) { - EJDB* ejdb = getEJDB(self); - ejdbclose(ejdb); + ejdbclose(getEJDB(self)); } void EJDB_drop_collection(int argc, VALUE* argv, VALUE self) { @@ -138,7 +136,6 @@ void EJDB_drop_collection(int argc, VALUE* argv, VALUE self) { VALUE prune; rb_scan_args(argc, argv, "11", &collName, &prune); - SafeStringValue(collName); EJDB* ejdb = getEJDB(self); @@ -199,7 +196,7 @@ VALUE EJDB_save(int argc, VALUE *argv, VALUE self) { for (i = 1; i < argc; i++) { VALUE rbobj = argv[i]; - if (merge && i == argc - 1) break; + if (i == argc - 1 && (TYPE(rbobj) == T_TRUE || TYPE(rbobj) == T_FALSE)) break; if (NIL_P(rbobj)) { rb_ary_push(oids, Qnil); @@ -249,7 +246,7 @@ VALUE EJDB_load(VALUE self, VALUE collName, VALUE rboid) { bson_oid_t oid = ruby_to_bson_oid(rboid); bson *bs = ejdbloadbson(coll, &oid); - return bs ? bson_to_ruby(bs) : nil_or_raise_ejdb_error(ejdb); + return bs ? bson_to_ruby_ensure_destroy(bs) : nil_or_raise_ejdb_error(ejdb); } @@ -288,7 +285,6 @@ VALUE EJDB_remove_query_internal(RBEJDB_QUERY* rbquery) { } VALUE EJDB_query_free(RBEJDB_QUERY* rbquery) { - EJDB_remove_query_internal(rbquery); ruby_xfree(rbquery); } @@ -299,7 +295,7 @@ VALUE EJDB_find_internal(VALUE self, VALUE collName, VALUE queryWrap, VALUE q, V VALUE orarrlng = rb_funcall(orarr, rb_intern("length"), 0); rbquery->qbson = NULL; rbquery->hintsbson = NULL; - rbquery->orarrbson = NUM2INT(orarrlng) ? (bson*) tcmalloc(rbquery->orarrlng * sizeof(bson)) : NULL; + rbquery->orarrbson = NUM2INT(orarrlng) ? (bson*) malloc(NUM2INT(orarrlng) * sizeof(bson)) : NULL; rbquery->orarrlng = 0; ruby_to_bson(q, &(rbquery->qbson), RUBY_TO_BSON_AS_QUERY); @@ -391,6 +387,7 @@ VALUE EJDB_find(int argc, VALUE* argv, VALUE self) { rb_ary_push(params, orarr); rb_ary_push(params, hints); + // Even if exception raised during find() we will free memory, taken for query return rb_ensure(EJDB_find_internal_wrapper, params, EJDB_find_ensure, queryWrap); } diff --git a/rbejdb/test/t2.rb b/rbejdb/test/t2.rb index 264e850..e360451 100644 --- a/rbejdb/test/t2.rb +++ b/rbejdb/test/t2.rb @@ -502,6 +502,7 @@ class EJDBTestUnit < Test::Unit::TestCase $jb.save("monsters", nil) $jb.save("monsters", {:name => :abracadabra}, {:name => :chupacabra}) $jb.save("monsters", self) + $jb.save("monsters", nil, false) #puts $jb.find("monsters").to_a.to_s assert_equal(6, $jb.find("monsters").count)