-
Notifications
You must be signed in to change notification settings - Fork 36
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
Add a Fiddle::Pointer#call_free method #36
Conversation
ext/fiddle/pointer.c
Outdated
@@ -24,6 +24,7 @@ struct ptr_data { | |||
void *ptr; | |||
long size; | |||
freefunc_t free; | |||
int freed; |
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.
Is int
and 1
and 0
the right way to do booleans in C extensions? I found some other things but wasn't sure if they were internal for MRI?
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.
We can use bool
and true
and false
.
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.
This failed in all CI configurations when I tried it.
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.
We need #include <stdbool.h>
.
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.
Done.
Some of these Windows CI failures don't make sense to me - some say they can't find |
test/fiddle/test_pointer.rb
Outdated
ptr.free! | ||
assert ptr.freed? | ||
ptr.free! # you can safely run it again | ||
assert ptr.freed? |
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.
What do we think free!
and freed?
should do when there's no free function attached?
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.
How about doing nothing?
refute ptr.freed?
ptr.free!
refute ptr.freed?
ptr.free!
refute ptr.freed?
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.
Yes that's what we do at the moment. As long as everyone else also thinks that's fine I'll do that.
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.
Done.
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.
We can ignore the "windows mswin" failure for now.
See also: #35
ext/fiddle/pointer.c
Outdated
@@ -24,6 +24,7 @@ struct ptr_data { | |||
void *ptr; | |||
long size; | |||
freefunc_t free; | |||
int freed; |
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.
We can use bool
and true
and false
.
test/fiddle/test_pointer.rb
Outdated
begin | ||
assert_equal 10, ptr.size | ||
assert_equal free.to_i, ptr.free.to_i | ||
ensure |
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.
Why do we need this?
The ptr
is freed by GC when on of the above two assertions is failed, isn't it?
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.
In my mind, it's good practice to free a native, external resource as soon as you can. That's why we use blocks for example when opening a file, rather than letting the GC close it. In the future I hope to create a PR for a similar block scope for pointers as well.
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.
OK.
Could you revert these changes in this pull request? We can rewrite our test codes with the block scope free feature in the pull request.
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.
Remove the manual free, leave it relying on GC for now, and then switch to the block malloc that's coming in the next PR? Ok.
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.
Done.
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.
Yes.
test/fiddle/test_pointer.rb
Outdated
@@ -20,16 +20,28 @@ def test_malloc_free_func_int | |||
assert_equal free.to_i, Fiddle::RUBY_FREE.to_i | |||
|
|||
ptr = Pointer.malloc(10, free.to_i) | |||
assert_equal 10, ptr.size | |||
assert_equal free.to_i, ptr.free.to_i | |||
refute ptr.freed? |
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.
Do we need test freed?
here?
It seems that you add separated tests for it.
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.
Tidied up.
test/fiddle/test_pointer.rb
Outdated
ptr.free! | ||
assert ptr.freed? | ||
ptr.free! # you can safely run it again | ||
assert ptr.freed? |
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.
How about doing nothing?
refute ptr.freed?
ptr.free!
refute ptr.freed?
ptr.free!
refute ptr.freed?
test/fiddle/test_pointer.rb
Outdated
ptr = Pointer.malloc(4, Fiddle::RUBY_FREE) | ||
refute ptr.freed? | ||
ptr.free! | ||
assert ptr.freed? | ||
ptr.free! # you can safely run it again | ||
assert ptr.freed? | ||
GC.start # you can safely run the GC routine | ||
assert ptr.freed? |
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.
Could you split this case to another test?
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.
Done.
ext/fiddle/pointer.c
Outdated
@@ -370,6 +385,34 @@ rb_fiddle_ptr_free_get(VALUE self) | |||
return rb_fiddle_new_function(address, arg_types, ret_type); | |||
} | |||
|
|||
/* | |||
* call-seq: free! => nil |
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.
How about renaming to call_free
or something?
free
(getter) and free!
will confuse us.
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.
Yes... I was going for terseness, but I'm open to debating this.
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.
It's also a destructive operation, which fits the !
pattern.
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.
free!
is destructive but free!
is not the destructive version of free
.
The relation of them is different from the relation of String#gsub
and String#gsub!
.
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 feel like free!
is best due to being short and looking destructive. I'm open to changing it if you're very certain or other people agree.
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.
If we have block style free feature (Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) {|pointer| ...}
?), we'll not call this method manually.
So I don't think we should use shorter name for this method with potential confusion. (Some users may use pointer.free
wrongly to free a pointer but the pointer isn't freed. It causes a memory leak.)
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.
Ok I'll use call_free
.
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.
Done.
We can also ignore all Windows jobs... |
I apologize for not looking at the Windows logs in Actions. All Windows MinGW Ruby versions, 2.4 and later, have a vendored file, See #37, which disables the loading of EDIT: I just created a branch with this PR, then added the change in PR #37, and all tests passed (except mswin). See Actions run: |
Thanks! |
free!
runs the user's free function. It doesn't do anything if the pointer has already been freed. The GC won't call the user's free function if the user already manually freed. Another methodfreed?
tells the user if the pointer has already been freed.All together, these changes allow you to rely on the GC to definitely free your memory, meaning no leaks, but then also allows you to manually free as early as possible if you can, meaning deterministic deallocation, improving cache locality and and reducing peaks in memory use.