-
Notifications
You must be signed in to change notification settings - Fork 142
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
Io.js 3.0 support #35
Conversation
…ay, and I really don't have time to decypher why. :) @TooTallNate knows.
About https://github.com/unbornchikken/ref/commit/b8a93f9db835dcafe42910e4237f10f239770c2a: |
The buffer should not try to free the underlying memory when an empty FreeCallback is given. https://github.com/nodejs/node/blob/master/src/node_buffer.cc#L326-L337 |
It is not possible that it tries to free the memory when Buffer's length is non zero despite there is a callback? Somehow Buffer internal callback throws a null ref exception when those "wrapped" pointers GCed and the memory gets corrupted. |
cc @trevnorris On August 18, 2015 11:32:35 PM EEST, "Gábor Mező" notifications@github.com wrote:
|
@kkoopa, @trevnorris sorry for guessing there, I didn't have too mutch time for investigating this. Later today I'm gonna build an io.js 3.0 from source and do a bit of debugging, and I can provide you the stack trace and maybe some more clues on this. |
If a callback is passed then node does nothing for memory management. It should be explicitly free'd by the user. And that is some strange black magic going on. Using a Buffer to store Persistent objects. |
Hi, @trevnorris , thanks for feedback. Please do not concentrate on that Buffer to Persistent "black magic" code for now, because the actual issue is not originating from there, because if I skip its tests, then mocha still crashes with segfault. So I did a bit debugging tonight, and the situation is the following: We create a buffer like:
Then we create an other Buffer holding a pointer to the JS side crated Buffer's char* value, see there: https://github.com/unbornchikken/ref/blob/io.js-3.0-support/src/binding.cc#L249 By calling our WrapPointer method: https://github.com/unbornchikken/ref/blob/io.js-3.0-support/src/binding.cc#L145 So we're end up having two buffers holding the same char*, and the owner of this pointer that should free it is the JS side created, the other one just hold it. The problem is, if a GC invoked when we're having this two instances in the memory, iojs 3.0 are gonna crash for good. It looks like the new Buffer implementation somehow touches its stuff behind that char* during GC, and it crashes because the original Buffer's destructor already freed that. Older versions are not affected. There is the stack trace:
|
I gues that VisitPointers method is about freeing custom memory blocks contained by Buffers, right? |
How was the Buffer passed to |
Yeah, I'm thinking about that too. I'm just creating a PR there, so dunno, but I'm gonna take a look at that tomorrow. |
Read/WritePointer's target is created by calling: new Buffer(sizeOfPointer) So it has enough bytes those capable to hold a pointer to data that the original instance is having. I cannot see there anything extraordinary, that module should work as is and as with older runtimes. |
@trevnorris to sum it up, the issue by my understanding is that we have two separate Buffers pointing to exactly the same memory location, and one of the Buffers is the owner who should free the memory, and the other is just referencing that memory (created with WrapPointer). GC will crash in this case. In the stack trace I can see some Map related code. Might there be some deallocating logic that maps to-be-freed buffers by using those internal memory addresses for the mapping key? |
if indirection > 1 buffer lenght should be pointer size
This should be the relevant part of the Buffer code. IIRC Buffer was reimplemented using ArrayBuffer in io.js 3.0. Something in this change has led to this new behavior, since everything worked in previous versions of Node / io.js. |
Well, by the first look that's make sense, I can see no issue there. I'm gonna launch a debugger with the actual situation, and see the stuff around there. |
So, I've isolated the repro case for this, it's in test/iojs3issue.js. Pretty weird stuff is happening: describe('iojs3issue', function () {
it('should not crash', function() {
for (var i = 0; i < 10; i++) {
gc()
var buf = new Buffer(8)
buf.fill(0)
var buf2 = ref.ref(buf)
var buf3 = ref.deref(buf2)
}
})
it('should crash', function() {
for (var i = 0; i < 10; i++) {
gc()
var buf = new Buffer(7)
buf.fill(0)
var buf2 = ref.ref(buf)
var buf3 = ref.deref(buf2)
}
})
}) The second one crashes on x64. The only difference is the size of data in the original buffer. It seems, the new Buffer or its backend specialized v8 array is doing some optimizations for the case when the data fits in the size of a pointer. @kkoopa @trevnorris do you know something about this? |
And I've also noticed some weird race condition to apply, because when I modified the loop end condition to 2, then there wasn't crash. The crash trigger threshold is looks like 3. |
I think it grew beyond the scope of this PR, so I've opened an issue there: nodejs/node#2484 |
Can I see the internal values at This very well might be a limitation of using the new implementation. If some internal Map depends on the pointer, even if the memory is externalized, then there's no way I can think of to emulate the old behavior. Only thing I think you could do is simply make a new typed array around the ArrayBuffer backing the other typed array. |
We gotta wait for this commit to land to the main release to finish this PR: nodejs/node#2487 This resolves our issue there. |
So, it's completed. |
Dunno why Appveyor'd got that braincock, all tests got passed on Windows at my side. |
+1 On Wed, Aug 26, 2015 at 7:25 AM, Gábor Mező notifications@github.com
|
Could you release this please? I've got some other dependencies than node-ff too. |
@unbornchikken v1.1.0 published to npm! |
So, I've made tweaks based on @kkoopa's suggestions in #33. Also replaced explicit NewBuffer calls with the WrapBuffer method by @kkoopa at ffi's node-ffi/node-ffi#221.
Unfortunatelly it still segafults. I know where, but I dunno why. It segfaults during a GC call when there is a living Buffer instances in the memory created by WrapBuffer, for eample there: https://github.com/unbornchikken/ref/blob/io.js-3.0-support/src/binding.cc#L256 By my understanding it looks like Buffer tries to free its pointer despite there is a free callback that is an empty function. @kkoopa, is some additional magic required to tell to a Buffer that there is no need to free its pointer? Or there is some other issue that we're facing here?