Change ArrayBuffer API and implementation to use embedder-provided allocator.

R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/15855012

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15056 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2013-06-11 10:41:14 +00:00
parent b90bd6987b
commit 9278a4b7b1
8 changed files with 130 additions and 51 deletions

View File

@ -2370,30 +2370,6 @@ class V8EXPORT Function : public Object {
static void CheckCast(Value* obj); static void CheckCast(Value* obj);
}; };
/**
* The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer|
* populates an instance of this class with a pointer to data and byte length.
*
* |ArrayBufferContents| is the owner of its data. When an instance of
* this class is destructed, the |Data| is freed.
*
* This API is experimental and may change significantly.
*/
class V8EXPORT ArrayBufferContents {
public:
ArrayBufferContents() : data_(NULL), byte_length_(0) {}
~ArrayBufferContents();
void* Data() const { return data_; }
size_t ByteLength() const { return byte_length_; }
private:
void* data_;
size_t byte_length_;
friend class ArrayBuffer;
};
#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT #ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2 #define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
#endif #endif
@ -2404,6 +2380,53 @@ class V8EXPORT ArrayBufferContents {
*/ */
class V8EXPORT ArrayBuffer : public Object { class V8EXPORT ArrayBuffer : public Object {
public: public:
/**
* Allocator that V8 uses to allocate |ArrayBuffer|'s memory.
* The allocator is a global V8 setting. It should be set with
* V8::SetArrayBufferAllocator prior to creation of a first ArrayBuffer.
*
* This API is experimental and may change significantly.
*/
class V8EXPORT Allocator { // NOLINT
public:
virtual ~Allocator() {}
/**
* Allocate |length| bytes. Return NULL if allocation is not successful.
*/
virtual void* Allocate(size_t length) = 0;
/**
* Free the memory pointed to |data|. That memory is guaranteed to be
* previously allocated by |Allocate|.
*/
virtual void Free(void* data) = 0;
};
/**
* The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer|
* returns an instance of this class, populated, with a pointer to data
* and byte length.
*
* The Data pointer of ArrayBuffer::Contents is always allocated with
* Allocator::Allocate that is set with V8::SetArrayBufferAllocator.
*
* This API is experimental and may change significantly.
*/
class V8EXPORT Contents { // NOLINT
public:
Contents() : data_(NULL), byte_length_(0) {}
void* Data() const { return data_; }
size_t ByteLength() const { return byte_length_; }
private:
void* data_;
size_t byte_length_;
friend class ArrayBuffer;
};
/** /**
* Data length in bytes. * Data length in bytes.
*/ */
@ -2440,14 +2463,18 @@ class V8EXPORT ArrayBuffer : public Object {
void Neuter(); void Neuter();
/** /**
* Pass the ownership of this ArrayBuffer's backing store to * Make this ArrayBuffer external. The pointer to underlying memory block
* a given ArrayBufferContents. * and byte length are returned as |Contents| structure. After ArrayBuffer
* had been etxrenalized, it does no longer owns the memory block. The caller
* should take steps to free memory when it is no longer needed.
*
* The memory block is guaranteed to be allocated with |Allocator::Allocate|
* that has been set with V8::SetArrayBufferAllocator.
*/ */
void Externalize(ArrayBufferContents* contents); Contents Externalize();
V8_INLINE(static ArrayBuffer* Cast(Value* obj)); V8_INLINE(static ArrayBuffer* Cast(Value* obj));
static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
private: private:
@ -4193,6 +4220,14 @@ class V8EXPORT V8 {
static void SetAllowCodeGenerationFromStringsCallback( static void SetAllowCodeGenerationFromStringsCallback(
AllowCodeGenerationFromStringsCallback that); AllowCodeGenerationFromStringsCallback that);
/**
* Set allocator to use for ArrayBuffer memory.
* The allocator should be set only once. The allocator should be set
* before any code tha uses ArrayBuffers is executed.
* This allocator is used in all isolates.
*/
static void SetArrayBufferAllocator(ArrayBuffer::Allocator* allocator);
/** /**
* Ignore out-of-memory exceptions. * Ignore out-of-memory exceptions.
* *

View File

@ -5160,6 +5160,15 @@ void v8::V8::SetJitCodeEventHandler(
isolate->logger()->SetCodeEventHandler(options, event_handler); isolate->logger()->SetCodeEventHandler(options, event_handler);
} }
void v8::V8::SetArrayBufferAllocator(
ArrayBuffer::Allocator* allocator) {
if (!ApiCheck(i::V8::ArrayBufferAllocator() == NULL,
"v8::V8::SetArrayBufferAllocator",
"ArrayBufferAllocator might only be set once"))
return;
i::V8::SetArrayBufferAllocator(allocator);
}
bool v8::V8::Dispose() { bool v8::V8::Dispose() {
i::Isolate* isolate = i::Isolate::Current(); i::Isolate* isolate = i::Isolate::Current();
@ -6129,25 +6138,17 @@ bool v8::ArrayBuffer::IsExternal() const {
return Utils::OpenHandle(this)->is_external(); return Utils::OpenHandle(this)->is_external();
} }
v8::ArrayBufferContents::~ArrayBufferContents() { v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() {
free(data_);
data_ = NULL;
byte_length_ = 0;
}
void v8::ArrayBuffer::Externalize(ArrayBufferContents* contents) {
i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this); i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
ApiCheck(!obj->is_external(), ApiCheck(!obj->is_external(),
"v8::ArrayBuffer::Externalize", "v8::ArrayBuffer::Externalize",
"ArrayBuffer already externalized"); "ArrayBuffer already externalized");
obj->set_is_external(true); obj->set_is_external(true);
size_t byte_length = static_cast<size_t>(obj->byte_length()->Number()); size_t byte_length = static_cast<size_t>(obj->byte_length()->Number());
ApiCheck(contents->data_ == NULL, Contents contents;
"v8::ArrayBuffer::Externalize", contents.data_ = obj->backing_store();
"Externalizing into non-empty ArrayBufferContents"); contents.byte_length_ = byte_length;
contents->data_ = obj->backing_store(); return contents;
contents->byte_length_ = byte_length;
} }

View File

@ -1571,6 +1571,13 @@ static void EnableHarmonyTypedArraysViaCommandLine() {
#endif #endif
class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) { return malloc(length); }
virtual void Free(void* data) { free(data); }
};
int Shell::Main(int argc, char* argv[]) { int Shell::Main(int argc, char* argv[]) {
if (!SetOptions(argc, argv)) return 1; if (!SetOptions(argc, argv)) return 1;
#ifndef V8_SHARED #ifndef V8_SHARED
@ -1579,6 +1586,8 @@ int Shell::Main(int argc, char* argv[]) {
#else #else
EnableHarmonyTypedArraysViaCommandLine(); EnableHarmonyTypedArraysViaCommandLine();
#endif #endif
ShellArrayBufferAllocator array_buffer_allocator;
v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
int result = 0; int result = 0;
Isolate* isolate = Isolate::GetCurrent(); Isolate* isolate = Isolate::GetCurrent();
DumbLineEditor dumb_line_editor(isolate); DumbLineEditor dumb_line_editor(isolate);

View File

@ -663,7 +663,8 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate,
isolate, array_buffer->byte_length()); isolate, array_buffer->byte_length());
isolate->heap()->AdjustAmountOfExternalAllocatedMemory( isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
-static_cast<intptr_t>(allocated_length)); -static_cast<intptr_t>(allocated_length));
free(data); CHECK(V8::ArrayBufferAllocator() != NULL);
V8::ArrayBufferAllocator()->Free(data);
} }
object->Dispose(external_isolate); object->Dispose(external_isolate);
} }
@ -699,8 +700,9 @@ bool Runtime::SetupArrayBufferAllocatingData(
Handle<JSArrayBuffer> array_buffer, Handle<JSArrayBuffer> array_buffer,
size_t allocated_length) { size_t allocated_length) {
void* data; void* data;
CHECK(V8::ArrayBufferAllocator() != NULL);
if (allocated_length != 0) { if (allocated_length != 0) {
data = malloc(allocated_length); data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
if (data == NULL) return false; if (data == NULL) return false;
memset(data, 0, allocated_length); memset(data, 0, allocated_length);
} else { } else {

View File

@ -56,6 +56,7 @@ bool V8::has_been_disposed_ = false;
bool V8::has_fatal_error_ = false; bool V8::has_fatal_error_ = false;
bool V8::use_crankshaft_ = true; bool V8::use_crankshaft_ = true;
List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL; List<CallCompletedCallback>* V8::call_completed_callbacks_ = NULL;
v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL;
static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER; static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;

View File

@ -121,6 +121,15 @@ class V8 : public AllStatic {
static void RemoveCallCompletedCallback(CallCompletedCallback callback); static void RemoveCallCompletedCallback(CallCompletedCallback callback);
static void FireCallCompletedCallback(Isolate* isolate); static void FireCallCompletedCallback(Isolate* isolate);
static v8::ArrayBuffer::Allocator* ArrayBufferAllocator() {
return array_buffer_allocator_;
}
static void SetArrayBufferAllocator(v8::ArrayBuffer::Allocator *allocator) {
CHECK_EQ(NULL, array_buffer_allocator_);
array_buffer_allocator_ = allocator;
}
private: private:
static void InitializeOncePerProcessImpl(); static void InitializeOncePerProcessImpl();
static void InitializeOncePerProcess(); static void InitializeOncePerProcess();
@ -139,6 +148,8 @@ class V8 : public AllStatic {
static bool use_crankshaft_; static bool use_crankshaft_;
// List of callbacks when a Call completes. // List of callbacks when a Call completes.
static List<CallCompletedCallback>* call_completed_callbacks_; static List<CallCompletedCallback>* call_completed_callbacks_;
// Allocator for external array buffers.
static v8::ArrayBuffer::Allocator* array_buffer_allocator_;
}; };

View File

@ -98,10 +98,21 @@ static void PrintTestList(CcTest* current) {
v8::Isolate* CcTest::default_isolate_; v8::Isolate* CcTest::default_isolate_;
class CcTestArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) { return malloc(length); }
virtual void Free(void* data) { free(data); }
};
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true); v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
v8::internal::FLAG_harmony_array_buffer = true; v8::internal::FLAG_harmony_array_buffer = true;
v8::internal::FLAG_harmony_typed_arrays = true; v8::internal::FLAG_harmony_typed_arrays = true;
CcTestArrayBufferAllocator array_buffer_allocator;
v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
CcTest::set_default_isolate(v8::Isolate::GetCurrent()); CcTest::set_default_isolate(v8::Isolate::GetCurrent());
CHECK(CcTest::default_isolate() != NULL); CHECK(CcTest::default_isolate() != NULL);
int tests_run = 0; int tests_run = 0;

View File

@ -2570,6 +2570,19 @@ THREADED_TEST(SymbolProperties) {
} }
class ScopedArrayBufferContents {
public:
explicit ScopedArrayBufferContents(
const v8::ArrayBuffer::Contents& contents)
: contents_(contents) {}
~ScopedArrayBufferContents() { free(contents_.Data()); }
void* Data() const { return contents_.Data(); }
size_t ByteLength() const { return contents_.ByteLength(); }
private:
const v8::ArrayBuffer::Contents contents_;
};
THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
i::FLAG_harmony_array_buffer = true; i::FLAG_harmony_array_buffer = true;
i::FLAG_harmony_typed_arrays = true; i::FLAG_harmony_typed_arrays = true;
@ -2583,8 +2596,7 @@ THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
CHECK(!ab->IsExternal()); CHECK(!ab->IsExternal());
HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
v8::ArrayBufferContents ab_contents; ScopedArrayBufferContents ab_contents(ab->Externalize());
ab->Externalize(&ab_contents);
CHECK(ab->IsExternal()); CHECK(ab->IsExternal());
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
@ -2626,8 +2638,7 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result); Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
CHECK(!ab1->IsExternal()); CHECK(!ab1->IsExternal());
v8::ArrayBufferContents ab1_contents; ScopedArrayBufferContents ab1_contents(ab1->Externalize());
ab1->Externalize(&ab1_contents);
CHECK(ab1->IsExternal()); CHECK(ab1->IsExternal());
result = CompileRun("ab1.byteLength"); result = CompileRun("ab1.byteLength");
@ -2734,8 +2745,7 @@ THREADED_TEST(ArrayBuffer_NeuteringApi) {
v8::Handle<v8::Float64Array> f64a = v8::Handle<v8::Float64Array> f64a =
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127); CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
v8::ArrayBufferContents contents; ScopedArrayBufferContents contents(buffer->Externalize());
buffer->Externalize(&contents);
buffer->Neuter(); buffer->Neuter();
CHECK_EQ(0, static_cast<int>(buffer->ByteLength())); CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
CheckIsNeutered(u8a); CheckIsNeutered(u8a);
@ -2786,8 +2796,7 @@ THREADED_TEST(ArrayBuffer_NeuteringScript) {
v8::Handle<v8::Float64Array> f64a( v8::Handle<v8::Float64Array> f64a(
v8::Float64Array::Cast(*CompileRun("f64a"))); v8::Float64Array::Cast(*CompileRun("f64a")));
v8::ArrayBufferContents contents; ScopedArrayBufferContents contents(ab->Externalize());
ab->Externalize(&contents);
ab->Neuter(); ab->Neuter();
CHECK_EQ(0, static_cast<int>(ab->ByteLength())); CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
CheckIsNeutered(u8a); CheckIsNeutered(u8a);