diff --git a/src/smalloc.cc b/src/smalloc.cc index 1e54ec72f6..7d5c5d4984 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -173,6 +173,27 @@ void TargetCallback(Isolate* isolate, Persistent* target, char* data) { } +Handle AllocDispose(const Arguments& args) { + AllocDispose(args[0]->ToObject()); + return Undefined(node_isolate); +} + + +void AllocDispose(Handle obj) { + char* data = static_cast(obj->GetIndexedPropertiesExternalArrayData()); + size_t length = obj->GetIndexedPropertiesExternalArrayDataLength(); + + if (data == NULL || length == 0) + return; + + obj->SetIndexedPropertiesToExternalArrayData(NULL, + kExternalUnsignedByteArray, + 0); + delete[] data; + node_isolate->AdjustAmountOfExternalAllocatedMemory(-length); +} + + void Alloc(Handle obj, size_t length, FreeCallback fn, void* hint) { assert(length <= kMaxLength); @@ -286,6 +307,8 @@ void Initialize(Handle exports) { exports->Set(String::New("alloc"), FunctionTemplate::New(Alloc)->GetFunction()); + exports->Set(String::New("dispose"), + FunctionTemplate::New(AllocDispose)->GetFunction()); exports->Set(String::New("kMaxLength"), Uint32::New(kMaxLength, node_isolate)); diff --git a/src/smalloc.h b/src/smalloc.h index 88a9530af6..bfd41cabec 100644 --- a/src/smalloc.h +++ b/src/smalloc.h @@ -56,6 +56,12 @@ void Alloc(v8::Handle obj, FreeCallback fn, void* hint); +/** + * Free memory associated with an externally allocated object. If no external + * memory is allocated to the object then nothing will happen. + */ +void AllocDispose(v8::Handle obj); + } // namespace smalloc } // namespace node diff --git a/test/simple/test-smalloc-dispose-segfault.js b/test/simple/test-smalloc-dispose-segfault.js new file mode 100644 index 0000000000..22c8587535 --- /dev/null +++ b/test/simple/test-smalloc-dispose-segfault.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var spawn = require('child_process').spawn; +var smalloc = process.binding('smalloc'); +var alloc = smalloc.alloc; +var dispose = smalloc.dispose; + + +// child +if (process.argv[2] === 'child') { + + // test that disposing an allocation won't cause the MakeWeakCallback to try + // and free invalid memory + for (var i = 0; i < 1e4; i++) { + dispose(alloc({}, 5)); + if (i % 10 === 0) gc(); + } + +} else { + // test case + var child = spawn(process.execPath, + ['--expose_gc', __filename, 'child']); + + child.on('exit', function(code, signal) { + assert.equal(code, 0, signal); + console.log('dispose didn\'t segfault'); + }); +} diff --git a/test/simple/test-smalloc.js b/test/simple/test-smalloc.js index 14450afa44..2a306216a3 100644 --- a/test/simple/test-smalloc.js +++ b/test/simple/test-smalloc.js @@ -25,6 +25,7 @@ var assert = require('assert'); var smalloc = process.binding('smalloc'); var alloc = smalloc.alloc; var copyOnto = smalloc.copyOnto; +var dispose = smalloc.dispose; var sliceOnto = smalloc.sliceOnto; @@ -87,3 +88,11 @@ for (var i = 0; i < 6; i++) { assert.equal(c[i], i); assert.equal(c[i + 6], i * 2); } + + +// test disposal + +var b = alloc({}, 5); +dispose(b); +for (var i = 0; i < 5; i++) + assert.equal(b[i], undefined);