-
Notifications
You must be signed in to change notification settings - Fork 341
Implement Write Barrier and dsize for FFI::FunctionType #1001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,16 +53,19 @@ static VALUE fntype_allocate(VALUE klass); | |
| static VALUE fntype_initialize(int argc, VALUE* argv, VALUE self); | ||
| static void fntype_mark(void *); | ||
| static void fntype_free(void *); | ||
| static size_t fntype_memsize(const void *); | ||
|
|
||
| const rb_data_type_t rbffi_fntype_data_type = { /* extern */ | ||
| .wrap_struct_name = "FFI::FunctionType", | ||
| .function = { | ||
| .dmark = fntype_mark, | ||
| .dfree = fntype_free, | ||
| .dsize = NULL, | ||
| }, | ||
| .parent = &rbffi_type_data_type, | ||
| .flags = RUBY_TYPED_FREE_IMMEDIATELY | ||
| .wrap_struct_name = "FFI::FunctionType", | ||
| .function = { | ||
| .dmark = fntype_mark, | ||
| .dfree = fntype_free, | ||
| .dsize = fntype_memsize, | ||
| }, | ||
| .parent = &rbffi_type_data_type, | ||
| // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() | ||
| // macro to update VALUE references, as to trigger write barriers. | ||
| .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | ||
| }; | ||
|
|
||
| VALUE rbffi_FunctionTypeClass = Qnil; | ||
|
|
@@ -75,9 +78,9 @@ fntype_allocate(VALUE klass) | |
|
|
||
| fnInfo->type.ffiType = &ffi_type_pointer; | ||
| fnInfo->type.nativeType = NATIVE_FUNCTION; | ||
| fnInfo->rbReturnType = Qnil; | ||
| fnInfo->rbParameterTypes = Qnil; | ||
| fnInfo->rbEnums = Qnil; | ||
| RB_OBJ_WRITE(obj, &fnInfo->rbReturnType, Qnil); | ||
| RB_OBJ_WRITE(obj, &fnInfo->rbParameterTypes, Qnil); | ||
| RB_OBJ_WRITE(obj, &fnInfo->rbEnums, Qnil); | ||
| fnInfo->invoke = rbffi_CallFunction; | ||
| fnInfo->closurePool = NULL; | ||
|
|
||
|
|
@@ -110,6 +113,23 @@ fntype_free(void *data) | |
| xfree(fnInfo); | ||
| } | ||
|
|
||
| static size_t | ||
| fntype_memsize(const void *data) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I accounted for all held data, but worst case these function don't have to be 100% accurate.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A FunctionInfo can have a closurePool associated for all closures that are created in relation to this FunctionInfo. I'll add at least the management data of the ClosurePool to the size.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, I'll have a look at it. We canalso report the size of the pool divided by the number of refs to it, I've done that in |
||
| { | ||
| const FunctionType *fnInfo = (const FunctionType *)data; | ||
|
|
||
| size_t memsize = sizeof(FunctionType); | ||
| memsize += fnInfo->callbackCount * sizeof(VALUE); | ||
|
|
||
| memsize += fnInfo->parameterCount * ( | ||
| sizeof(*fnInfo->parameterTypes) | ||
| + sizeof(ffi_type *) | ||
| + sizeof(*fnInfo->nativeParameterTypes) | ||
| ); | ||
|
|
||
| return memsize; | ||
| } | ||
|
|
||
| /* | ||
| * call-seq: initialize(return_type, param_types, options={}) | ||
| * @param [Type, Symbol] return_type return type for the function | ||
|
|
@@ -147,8 +167,8 @@ fntype_initialize(int argc, VALUE* argv, VALUE self) | |
| fnInfo->parameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->parameterTypes)); | ||
| fnInfo->ffiParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(ffi_type *)); | ||
| fnInfo->nativeParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->nativeParameterTypes)); | ||
| fnInfo->rbParameterTypes = rb_ary_new2(fnInfo->parameterCount); | ||
| fnInfo->rbEnums = rbEnums; | ||
| RB_OBJ_WRITE(self, &fnInfo->rbParameterTypes, rb_ary_new2(fnInfo->parameterCount)); | ||
| RB_OBJ_WRITE(self, &fnInfo->rbEnums, rbEnums); | ||
| fnInfo->blocking = RTEST(rbBlocking); | ||
| fnInfo->hasStruct = false; | ||
|
|
||
|
|
@@ -163,7 +183,7 @@ fntype_initialize(int argc, VALUE* argv, VALUE self) | |
|
|
||
| if (rb_obj_is_kind_of(type, rbffi_FunctionTypeClass)) { | ||
| REALLOC_N(fnInfo->callbackParameters, VALUE, fnInfo->callbackCount + 1); | ||
| fnInfo->callbackParameters[fnInfo->callbackCount++] = type; | ||
| RB_OBJ_WRITE(self, &fnInfo->callbackParameters[fnInfo->callbackCount++], type); | ||
| } | ||
|
|
||
| if (rb_obj_is_kind_of(type, rbffi_StructByValueClass)) { | ||
|
|
@@ -176,7 +196,7 @@ fntype_initialize(int argc, VALUE* argv, VALUE self) | |
| fnInfo->nativeParameterTypes[i] = fnInfo->parameterTypes[i]->nativeType; | ||
| } | ||
|
|
||
| fnInfo->rbReturnType = rbffi_Type_Lookup(rbReturnType); | ||
| RB_OBJ_WRITE(self, &fnInfo->rbReturnType, rbffi_Type_Lookup(rbReturnType)); | ||
| if (!RTEST(fnInfo->rbReturnType)) { | ||
| VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL); | ||
| rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # | ||
| # This file is part of ruby-ffi. | ||
| # For licensing, see LICENSE.SPECS | ||
| # | ||
|
|
||
| require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) | ||
|
|
||
| describe "FFI::FunctionType", skip: RUBY_ENGINE != "ruby" do | ||
| it 'is initialized with return type and a list of parameter types' do | ||
| function_type = FFI::FunctionType.new(:int, [ :char, :ulong ]) | ||
| expect(function_type.result_type).to be == FFI::Type::Builtin::INT | ||
| expect(function_type.param_types).to be == [ FFI::Type::Builtin::CHAR, FFI::Type::Builtin::ULONG ] | ||
| end | ||
|
|
||
| it 'has a memsize function' do | ||
| base_size = ObjectSpace.memsize_of(Object.new) | ||
|
|
||
| function_type = FFI::FunctionType.new(:int, []) | ||
| size = ObjectSpace.memsize_of(function_type) | ||
| expect(size).to be > base_size | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The size can vary on platforms etc, so I usually use this pattern to ensure the function report something, but not assert a precise size. |
||
|
|
||
| base_size = size | ||
| function_type = FFI::FunctionType.new(:int, [:char]) | ||
| size = ObjectSpace.memsize_of(function_type) | ||
| expect(size).to be > base_size | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was asked to add this comment when I added write barriers in the protobuf gem, and I think it's a good idea as it might remind future contributors.