Commit 4f8761ca authored by binji's avatar binji Committed by Commit bot

Revert of Add d8 API for spawning function on a new thread (Second try)...

Revert of Add d8 API for spawning function on a new thread (Second try) (patchset #3 id:60001 of https://codereview.chromium.org/1195613003/)

Reason for revert:
Fails on V8 Linux - isolates (http://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20isolates/builds/4128)

Original issue's description:
> Add d8 API for spawning function on a new thread (Second try)
>
> This API closely matches the Worker API. The differences:
>
> 1) The argument to the Worker constructor is a function to run, not a script.
> 2) Receiving a message from a worker is a synchronous API (as there is no event
> loop).
>
> The serialization done here is not robust as the real DOM implementation. For
> example, recursive data structures or otherwise duplicated objects are not
> allowed.
>
> BUG=chromium:497295
> R=jochen@chromium.org
> LOG=n
>
> Review URL: https://codereview.chromium.org/1185643004
>
> Cr-Commit-Position: refs/heads/master@{#29126}
>
> Committed: https://crrev.com/ec2eaf712ecee6b4891c0458f2397e04a1f9b339
> Cr-Commit-Position: refs/heads/master@{#29158}

TBR=jochen@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=chromium:497295

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

Cr-Commit-Position: refs/heads/master@{#29161}
parent 87afca31
......@@ -46,7 +46,6 @@
#include "src/d8-debug.h"
#include "src/debug.h"
#include "src/snapshot/natives.h"
#include "src/utils.h"
#include "src/v8.h"
#endif // !V8_SHARED
......@@ -105,19 +104,6 @@ class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
v8::Platform* g_platform = NULL;
#ifndef V8_SHARED
bool FindInObjectList(Handle<Object> object, const Shell::ObjectList& list) {
for (int i = 0; i < list.length(); ++i) {
if (list[i]->StrictEquals(object)) {
return true;
}
}
return false;
}
#endif // !V8_SHARED
} // namespace
......@@ -205,12 +191,9 @@ base::Mutex Shell::context_mutex_;
const base::TimeTicks Shell::kInitialTicks =
base::TimeTicks::HighResolutionNow();
Persistent<Context> Shell::utility_context_;
Worker Shell::worker_;
i::List<SharedArrayBuffer::Contents> Shell::externalized_shared_contents_;
#endif // !V8_SHARED
Persistent<Context> Shell::evaluation_context_;
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
ShellOptions Shell::options;
const char* Shell::kPrompt = "d8> ";
......@@ -243,8 +226,9 @@ ScriptCompiler::CachedData* CompileForCachedData(
name_buffer = new uint16_t[name_length];
name_string->Write(name_buffer, 0, name_length);
}
ShellArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
create_params.array_buffer_allocator = &allocator;
Isolate* temp_isolate = Isolate::New(create_params);
ScriptCompiler::CachedData* result = NULL;
{
......@@ -677,86 +661,6 @@ void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
#ifndef V8_SHARED
void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
if (args.Length() < 1 || !args[0]->IsFunction()) {
Throw(args.GetIsolate(), "1st argument must be function");
return;
}
String::Utf8Value function_string(args[0]->ToString());
worker_.StartExecuteInThread(isolate, *function_string);
}
void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() < 1) {
Throw(isolate, "Invalid argument");
return;
}
Handle<Value> message = args[0];
ObjectList to_transfer;
if (args.Length() >= 2) {
if (!args[1]->IsArray()) {
Throw(isolate, "Transfer list must be an Array");
return;
}
Handle<Array> transfer = Handle<Array>::Cast(args[1]);
uint32_t length = transfer->Length();
for (uint32_t i = 0; i < length; ++i) {
Handle<Value> element;
if (transfer->Get(context, i).ToLocal(&element)) {
if (!element->IsArrayBuffer() && !element->IsSharedArrayBuffer()) {
Throw(isolate,
"Transfer array elements must be an ArrayBuffer or "
"SharedArrayBuffer.");
break;
}
to_transfer.Add(Handle<Object>::Cast(element));
}
}
}
ObjectList seen_objects;
SerializationData* data = new SerializationData;
if (SerializeValue(isolate, message, to_transfer, &seen_objects, data)) {
worker_.PostMessage(data);
} else {
delete data;
}
}
void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
SerializationData* data = worker_.GetMessage();
if (data) {
int offset = 0;
Local<Value> data_value;
if (Shell::DeserializeValue(isolate, *data, &offset).ToLocal(&data_value)) {
args.GetReturnValue().Set(data_value);
}
delete data;
}
}
void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
worker_.Terminate();
}
#endif // !V8_SHARED
void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
int exit_code = args[0]->Int32Value();
OnExit(args.GetIsolate());
......@@ -1088,20 +992,6 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
FunctionTemplate::New(isolate, PerformanceNow));
global_template->Set(String::NewFromUtf8(isolate, "performance"),
performance_template);
Handle<FunctionTemplate> worker_fun_template =
FunctionTemplate::New(isolate, WorkerNew);
worker_fun_template->PrototypeTemplate()->Set(
String::NewFromUtf8(isolate, "terminate"),
FunctionTemplate::New(isolate, WorkerTerminate));
worker_fun_template->PrototypeTemplate()->Set(
String::NewFromUtf8(isolate, "postMessage"),
FunctionTemplate::New(isolate, WorkerPostMessage));
worker_fun_template->PrototypeTemplate()->Set(
String::NewFromUtf8(isolate, "getMessage"),
FunctionTemplate::New(isolate, WorkerGetMessage));
global_template->Set(String::NewFromUtf8(isolate, "Worker"),
worker_fun_template);
#endif // !V8_SHARED
Handle<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
......@@ -1435,8 +1325,9 @@ base::Thread::Options SourceGroup::GetThreadOptions() {
void SourceGroup::ExecuteInThread() {
ShellArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
create_params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::New(create_params);
do {
next_semaphore_.Wait();
......@@ -1478,253 +1369,6 @@ void SourceGroup::WaitForThread() {
done_semaphore_.Wait();
}
}
SerializationData::~SerializationData() {
// Any ArrayBuffer::Contents are owned by this SerializationData object.
// SharedArrayBuffer::Contents may be used by other threads, so must be
// cleaned up by the main thread in Shell::CleanupWorkers().
for (int i = 0; i < array_buffer_contents.length(); ++i) {
ArrayBuffer::Contents& contents = array_buffer_contents[i];
Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength());
}
}
void SerializationData::WriteTag(SerializationTag tag) { data.Add(tag); }
void SerializationData::WriteMemory(const void* p, int length) {
i::Vector<uint8_t> block = data.AddBlock(0, length);
memcpy(&block[0], p, length);
}
void SerializationData::WriteArrayBufferContents(
const ArrayBuffer::Contents& contents) {
array_buffer_contents.Add(contents);
WriteTag(kSerializationTagTransferredArrayBuffer);
int index = array_buffer_contents.length() - 1;
Write(index);
}
void SerializationData::WriteSharedArrayBufferContents(
const SharedArrayBuffer::Contents& contents) {
shared_array_buffer_contents.Add(contents);
WriteTag(kSerializationTagTransferredSharedArrayBuffer);
int index = shared_array_buffer_contents.length() - 1;
Write(index);
}
SerializationTag SerializationData::ReadTag(int* offset) const {
return static_cast<SerializationTag>(Read<uint8_t>(offset));
}
void SerializationData::ReadMemory(void* p, int length, int* offset) const {
memcpy(p, &data[*offset], length);
(*offset) += length;
}
void SerializationData::ReadArrayBufferContents(ArrayBuffer::Contents* contents,
int* offset) const {
int index = Read<int>(offset);
DCHECK(index < array_buffer_contents.length());
*contents = array_buffer_contents[index];
}
void SerializationData::ReadSharedArrayBufferContents(
SharedArrayBuffer::Contents* contents, int* offset) const {
int index = Read<int>(offset);
DCHECK(index < shared_array_buffer_contents.length());
*contents = shared_array_buffer_contents[index];
}
void SerializationDataQueue::Enqueue(SerializationData* data) {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
data_.Add(data);
}
bool SerializationDataQueue::Dequeue(SerializationData** data) {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
if (data_.is_empty()) return false;
*data = data_.Remove(0);
return true;
}
bool SerializationDataQueue::IsEmpty() {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
return data_.is_empty();
}
void SerializationDataQueue::Clear() {
base::LockGuard<base::Mutex> lock_guard(&mutex_);
for (int i = 0; i < data_.length(); ++i) {
delete data_[i];
}
data_.Clear();
}
Worker::Worker()
: in_semaphore_(0), out_semaphore_(0), thread_(NULL), script_(NULL) {}
Worker::~Worker() { Cleanup(); }
void Worker::StartExecuteInThread(Isolate* isolate,
const char* function_string) {
if (thread_) {
Throw(isolate, "Only one worker allowed");
return;
}
static const char format[] = "(%s).call(this);";
size_t len = strlen(function_string) + sizeof(format);
script_ = new char[len + 1];
i::Vector<char> vec(script_, static_cast<int>(len + 1));
i::SNPrintF(vec, format, function_string);
thread_ = new WorkerThread(this);
thread_->Start();
}
void Worker::PostMessage(SerializationData* data) {
in_queue_.Enqueue(data);
in_semaphore_.Signal();
}
SerializationData* Worker::GetMessage() {
SerializationData* data;
while (!out_queue_.Dequeue(&data)) {
out_semaphore_.Wait();
}
return data;
}
void Worker::Terminate() {
if (thread_ == NULL) return;
PostMessage(NULL);
thread_->Join();
Cleanup();
}
void Worker::ExecuteInThread() {
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope iscope(isolate);
{
HandleScope scope(isolate);
PerIsolateData data(isolate);
Local<Context> context = Shell::CreateEvaluationContext(isolate);
{
Context::Scope cscope(context);
PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
Handle<Object> global = context->Global();
Handle<Value> this_value = External::New(isolate, this);
Handle<FunctionTemplate> postmessage_fun_template =
FunctionTemplate::New(isolate, PostMessageOut, this_value);
Handle<Function> postmessage_fun;
if (postmessage_fun_template->GetFunction(context)
.ToLocal(&postmessage_fun)) {
global->Set(String::NewFromUtf8(isolate, "postMessage"),
postmessage_fun);
}
// First run the script
Handle<String> file_name = String::NewFromUtf8(isolate, "unnamed");
Handle<String> source = String::NewFromUtf8(isolate, script_);
Shell::ExecuteString(isolate, source, file_name, false, true);
// Get the message handler
Handle<Value> onmessage =
global->Get(String::NewFromUtf8(isolate, "onmessage"));
if (onmessage->IsFunction()) {
Handle<Function> onmessage_fun = Handle<Function>::Cast(onmessage);
// Now wait for messages
bool done = false;
while (!done) {
in_semaphore_.Wait();
SerializationData* data;
if (!in_queue_.Dequeue(&data)) continue;
if (data == NULL) {
done = true;
break;
}
int offset = 0;
Local<Value> data_value;
if (Shell::DeserializeValue(isolate, *data, &offset)
.ToLocal(&data_value)) {
Handle<Value> argv[] = {data_value};
(void)onmessage_fun->Call(context, global, 1, argv);
}
delete data;
}
}
}
}
}
Shell::CollectGarbage(isolate);
isolate->Dispose();
}
void Worker::Cleanup() {
delete thread_;
thread_ = NULL;
delete[] script_;
script_ = NULL;
in_queue_.Clear();
out_queue_.Clear();
}
void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
if (args.Length() < 1) {
Throw(isolate, "Invalid argument");
return;
}
Handle<Value> message = args[0];
// TODO(binji): Allow transferring from worker to main thread?
Shell::ObjectList to_transfer;
Shell::ObjectList seen_objects;
SerializationData* data = new SerializationData;
if (Shell::SerializeValue(isolate, message, to_transfer, &seen_objects,
data)) {
DCHECK(args.Data()->IsExternal());
Handle<External> this_value = Handle<External>::Cast(args.Data());
Worker* worker = static_cast<Worker*>(this_value->Value());
worker->out_queue_.Enqueue(data);
worker->out_semaphore_.Signal();
} else {
delete data;
}
}
#endif // !V8_SHARED
......@@ -1899,7 +1543,6 @@ int Shell::RunMain(Isolate* isolate, int argc, char* argv[]) {
for (int i = 1; i < options.num_isolates; ++i) {
options.isolate_sources[i].WaitForThread();
}
CleanupWorkers();
#endif // !V8_SHARED
return 0;
}
......@@ -1922,242 +1565,6 @@ void Shell::CollectGarbage(Isolate* isolate) {
#ifndef V8_SHARED
bool Shell::SerializeValue(Isolate* isolate, Handle<Value> value,
const ObjectList& to_transfer,
ObjectList* seen_objects,
SerializationData* out_data) {
DCHECK(out_data);
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
if (value->IsUndefined()) {
out_data->WriteTag(kSerializationTagUndefined);
} else if (value->IsNull()) {
out_data->WriteTag(kSerializationTagNull);
} else if (value->IsTrue()) {
out_data->WriteTag(kSerializationTagTrue);
} else if (value->IsFalse()) {
out_data->WriteTag(kSerializationTagFalse);
} else if (value->IsNumber()) {
Handle<Number> num = Handle<Number>::Cast(value);
double value = num->Value();
out_data->WriteTag(kSerializationTagNumber);
out_data->Write(value);
} else if (value->IsString()) {
v8::String::Utf8Value str(value);
out_data->WriteTag(kSerializationTagString);
out_data->Write(str.length());
out_data->WriteMemory(*str, str.length());
} else if (value->IsArray()) {
Handle<Array> array = Handle<Array>::Cast(value);
if (FindInObjectList(array, *seen_objects)) {
Throw(isolate, "Duplicated arrays not supported");
return false;
}
seen_objects->Add(array);
out_data->WriteTag(kSerializationTagArray);
uint32_t length = array->Length();
out_data->Write(length);
for (uint32_t i = 0; i < length; ++i) {
Local<Value> element_value;
if (array->Get(context, i).ToLocal(&element_value)) {
if (!SerializeValue(isolate, element_value, to_transfer, seen_objects,
out_data))
return false;
}
}
} else if (value->IsArrayBuffer()) {
Handle<ArrayBuffer> array_buffer = Handle<ArrayBuffer>::Cast(value);
if (FindInObjectList(array_buffer, *seen_objects)) {
Throw(isolate, "Duplicated array buffers not supported");
return false;
}
seen_objects->Add(array_buffer);
if (FindInObjectList(array_buffer, to_transfer)) {
// Transfer ArrayBuffer
if (!array_buffer->IsNeuterable()) {
Throw(isolate, "Attempting to transfer an un-neuterable ArrayBuffer");
return false;
}
ArrayBuffer::Contents contents = array_buffer->Externalize();
array_buffer->Neuter();
out_data->WriteArrayBufferContents(contents);
} else {
ArrayBuffer::Contents contents = array_buffer->GetContents();
// Clone ArrayBuffer
if (contents.ByteLength() > i::kMaxUInt32) {
Throw(isolate, "ArrayBuffer is too big to clone");
return false;
}
int byte_length = static_cast<int>(contents.ByteLength());
out_data->WriteTag(kSerializationTagArrayBuffer);
out_data->Write(byte_length);
out_data->WriteMemory(contents.Data(),
static_cast<int>(contents.ByteLength()));
}
} else if (value->IsSharedArrayBuffer()) {
Handle<SharedArrayBuffer> sab = Handle<SharedArrayBuffer>::Cast(value);
if (FindInObjectList(sab, *seen_objects)) {
Throw(isolate, "Duplicated shared array buffers not supported");
return false;
}
seen_objects->Add(sab);
if (!FindInObjectList(sab, to_transfer)) {
Throw(isolate, "SharedArrayBuffer must be transferred");
return false;
}
SharedArrayBuffer::Contents contents = sab->Externalize();
out_data->WriteSharedArrayBufferContents(contents);
externalized_shared_contents_.Add(contents);
} else if (value->IsObject()) {
Handle<Object> object = Handle<Object>::Cast(value);
if (FindInObjectList(object, *seen_objects)) {
Throw(isolate, "Duplicated objects not supported");
return false;
}
seen_objects->Add(object);
Local<Array> property_names;
if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
Throw(isolate, "Unable to get property names");
return false;
}
uint32_t length = property_names->Length();
out_data->WriteTag(kSerializationTagObject);
out_data->Write(length);
for (uint32_t i = 0; i < length; ++i) {
Handle<Value> name;
Handle<Value> property_value;
if (property_names->Get(context, i).ToLocal(&name) &&
object->Get(context, name).ToLocal(&property_value)) {
if (!SerializeValue(isolate, name, to_transfer, seen_objects, out_data))
return false;
if (!SerializeValue(isolate, property_value, to_transfer, seen_objects,
out_data))
return false;
}
}
} else {
Throw(isolate, "Don't know how to serialize object");
return false;
}
return true;
}
MaybeLocal<Value> Shell::DeserializeValue(Isolate* isolate,
const SerializationData& data,
int* offset) {
DCHECK(offset);
EscapableHandleScope scope(isolate);
// This function should not use utility_context_ because it is running on a
// different thread.
Local<Value> result;
SerializationTag tag = data.ReadTag(offset);
switch (tag) {
case kSerializationTagUndefined:
result = Undefined(isolate);
break;
case kSerializationTagNull:
result = Null(isolate);
break;
case kSerializationTagTrue:
result = True(isolate);
break;
case kSerializationTagFalse:
result = False(isolate);
break;
case kSerializationTagNumber:
result = Number::New(isolate, data.Read<double>(offset));
break;
case kSerializationTagString: {
int length = data.Read<int>(offset);
static char s_buffer[128];
char* p = s_buffer;
bool allocated = false;
if (length > static_cast<int>(sizeof(s_buffer))) {
p = new char[length];
allocated = true;
}
data.ReadMemory(p, length, offset);
MaybeLocal<String> str =
String::NewFromUtf8(isolate, p, String::kNormalString, length);
if (!str.IsEmpty()) result = str.ToLocalChecked();
if (allocated) delete[] p;
break;
}
case kSerializationTagArray: {
uint32_t length = data.Read<uint32_t>(offset);
Handle<Array> array = Array::New(isolate, length);
for (uint32_t i = 0; i < length; ++i) {
Local<Value> element_value;
CHECK(DeserializeValue(isolate, data, offset).ToLocal(&element_value));
array->Set(i, element_value);
}
result = array;
break;
}
case kSerializationTagObject: {
int length = data.Read<int>(offset);
Handle<Object> object = Object::New(isolate);
for (int i = 0; i < length; ++i) {
Local<Value> property_name;
CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_name));
DCHECK(property_name->IsString());
Local<Value> property_value;
CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_value));
object->Set(property_name, property_value);
}
result = object;
break;
}
case kSerializationTagArrayBuffer: {
int byte_length = data.Read<int>(offset);
Handle<ArrayBuffer> array_buffer = ArrayBuffer::New(isolate, byte_length);
ArrayBuffer::Contents contents = array_buffer->GetContents();
DCHECK(static_cast<size_t>(byte_length) == contents.ByteLength());
data.ReadMemory(contents.Data(), byte_length, offset);
result = array_buffer;
break;
}
case kSerializationTagTransferredArrayBuffer: {
ArrayBuffer::Contents contents;
data.ReadArrayBufferContents(&contents, offset);
result =
ArrayBuffer::New(isolate, contents.Data(), contents.ByteLength());
break;
}
case kSerializationTagTransferredSharedArrayBuffer: {
SharedArrayBuffer::Contents contents;
data.ReadSharedArrayBufferContents(&contents, offset);
result = SharedArrayBuffer::New(isolate, contents.Data(),
contents.ByteLength());
break;
}
default:
UNREACHABLE();
}
return scope.Escape(result);
}
void Shell::CleanupWorkers() {
worker_.Terminate();
for (int i = 0; i < externalized_shared_contents_.length(); ++i) {
const SharedArrayBuffer::Contents& contents =
externalized_shared_contents_[i];
Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength());
}
externalized_shared_contents_.Clear();
}
static void DumpHeapConstants(i::Isolate* isolate) {
i::Heap* heap = isolate->heap();
......@@ -2244,14 +1651,13 @@ int Shell::Main(int argc, char* argv[]) {
SetFlagsFromString("--redirect-code-traces-to=code.asm");
int result = 0;
Isolate::CreateParams create_params;
ShellArrayBufferAllocator shell_array_buffer_allocator;
ShellArrayBufferAllocator array_buffer_allocator;
MockArrayBufferAllocator mock_arraybuffer_allocator;
if (options.mock_arraybuffer_allocator) {
Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
create_params.array_buffer_allocator = &mock_arraybuffer_allocator;
} else {
Shell::array_buffer_allocator = &shell_array_buffer_allocator;
create_params.array_buffer_allocator = &array_buffer_allocator;
}
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
#if !defined(V8_SHARED) && defined(ENABLE_GDB_JIT_INTERFACE)
if (i::FLAG_gdbjit) {
create_params.code_event_handler = i::GDBJITInterface::EventHandler;
......
......@@ -8,7 +8,6 @@
#ifndef V8_SHARED
#include "src/allocation.h"
#include "src/hashmap.h"
#include "src/list.h"
#include "src/smart-pointers.h"
#include "src/v8.h"
#else
......@@ -168,108 +167,6 @@ class SourceGroup {
int end_offset_;
};
#ifndef V8_SHARED
enum SerializationTag {
kSerializationTagUndefined,
kSerializationTagNull,
kSerializationTagTrue,
kSerializationTagFalse,
kSerializationTagNumber,
kSerializationTagString,
kSerializationTagArray,
kSerializationTagObject,
kSerializationTagArrayBuffer,
kSerializationTagTransferredArrayBuffer,
kSerializationTagTransferredSharedArrayBuffer,
};
class SerializationData {
public:
SerializationData() {}
~SerializationData();
void WriteTag(SerializationTag tag);
void WriteMemory(const void* p, int length);
void WriteArrayBufferContents(const ArrayBuffer::Contents& contents);
void WriteSharedArrayBufferContents(
const SharedArrayBuffer::Contents& contents);
template <typename T>
void Write(const T& data) {
WriteMemory(&data, sizeof(data));
}
SerializationTag ReadTag(int* offset) const;
void ReadMemory(void* p, int length, int* offset) const;
void ReadArrayBufferContents(ArrayBuffer::Contents* contents,
int* offset) const;
void ReadSharedArrayBufferContents(SharedArrayBuffer::Contents* contents,
int* offset) const;
template <typename T>
T Read(int* offset) const {
T value;
ReadMemory(&value, sizeof(value), offset);
return value;
}
private:
i::List<uint8_t> data;
i::List<ArrayBuffer::Contents> array_buffer_contents;
i::List<SharedArrayBuffer::Contents> shared_array_buffer_contents;
};
class SerializationDataQueue {
public:
void Enqueue(SerializationData* data);
bool Dequeue(SerializationData** data);
bool IsEmpty();
void Clear();
private:
base::Mutex mutex_;
i::List<SerializationData*> data_;
};
class Worker {
public:
Worker();
~Worker();
void StartExecuteInThread(Isolate* isolate, const char* function_string);
void PostMessage(SerializationData* data);
SerializationData* GetMessage();
void Terminate();
private:
class WorkerThread : public base::Thread {
public:
explicit WorkerThread(Worker* worker)
: base::Thread(base::Thread::Options("WorkerThread")),
worker_(worker) {}
virtual void Run() { worker_->ExecuteInThread(); }
private:
Worker* worker_;
};
void ExecuteInThread();
void Cleanup();
static void PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args);
base::Semaphore in_semaphore_;
base::Semaphore out_semaphore_;
SerializationDataQueue in_queue_;
SerializationDataQueue out_queue_;
base::Thread* thread_;
char* script_;
};
#endif // !V8_SHARED
class ShellOptions {
public:
......@@ -349,17 +246,6 @@ class Shell : public i::AllStatic {
static void CollectGarbage(Isolate* isolate);
#ifndef V8_SHARED
// TODO(binji): stupid implementation for now. Is there an easy way to hash an
// object for use in i::HashMap? By pointer?
typedef i::List<Handle<Object>> ObjectList;
static bool SerializeValue(Isolate* isolate, Handle<Value> value,
const ObjectList& to_transfer,
ObjectList* seen_objects,
SerializationData* out_data);
static MaybeLocal<Value> DeserializeValue(Isolate* isolate,
const SerializationData& data,
int* offset);
static void CleanupWorkers();
static Handle<Array> GetCompletions(Isolate* isolate,
Handle<String> text,
Handle<String> full);
......@@ -403,11 +289,6 @@ class Shell : public i::AllStatic {
args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate()));
}
static void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerPostMessage(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args);
// The OS object on the global object contains methods for performing
// operating system calls:
//
......@@ -447,7 +328,6 @@ class Shell : public i::AllStatic {
static const char* kPrompt;
static ShellOptions options;
static ArrayBuffer::Allocator* array_buffer_allocator;
private:
static Persistent<Context> evaluation_context_;
......@@ -461,8 +341,6 @@ class Shell : public i::AllStatic {
static base::OS::MemoryMappedFile* counters_file_;
static base::Mutex context_mutex_;
static const base::TimeTicks kInitialTicks;
static Worker worker_;
static i::List<SharedArrayBuffer::Contents> externalized_shared_contents_;
static Counter* GetCounter(const char* name, bool is_histogram);
static void InstallUtilityScript(Isolate* isolate);
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test the Worker API of d8. This test only makes sense with d8. A Worker
// spawns a new OS thread and isolate, and runs it concurrently with the
// current running thread.
function f() {
postMessage("Starting worker");
// Set a global variable; should not be visible outside of the worker's
// context.
foo = 100;
var c = 0;
onmessage = function(m) {
switch (c++) {
case 0:
if (m !== undefined) throw new Error("undefined");
break;
case 1:
if (m !== null) throw new Error("null");
break;
case 2:
if (m !== true) throw new Error("true");
break;
case 3:
if (m !== false) throw new Error("false");
break;
case 4:
if (m !== 100) throw new Error("Number");
break;
case 5:
if (m !== "hi") throw new Error("String");
break;
case 6:
if (JSON.stringify(m) !== '[4,true,"bye"]') throw new Error("Array");
break;
case 7:
if (JSON.stringify(m) !== '{"a":1,"b":2.5,"c":"three"}')
throw new Error("Object");
break;
case 8:
var ab = m;
var t = new Uint32Array(ab);
if (ab.byteLength !== 16)
throw new Error("ArrayBuffer clone byteLength");
for (var i = 0; i < 4; ++i)
if (t[i] !== i)
throw new Error("ArrayBuffer clone value " + i);
break;
case 9:
var ab = m;
var t = new Uint32Array(ab);
if (ab.byteLength !== 32)
throw new Error("ArrayBuffer transfer byteLength");
for (var i = 0; i < 8; ++i)
if (t[i] !== i)
throw new Error("ArrayBuffer transfer value " + i);
break;
}
if (c == 10) {
postMessage("DONE");
}
}
}
if (this.Worker) {
function createArrayBuffer(byteLength) {
var ab = new ArrayBuffer(byteLength);
var t = new Uint32Array(ab);
for (var i = 0; i < byteLength / 4; ++i)
t[i] = i;
return ab;
}
var w = new Worker(f);
assertEquals("Starting worker", w.getMessage());
// Currently can only create one worker at a time.
assertThrows(function() { new Worker(f); });
w.postMessage(undefined);
w.postMessage(null);
w.postMessage(true);
w.postMessage(false);
w.postMessage(100);
w.postMessage("hi");
w.postMessage([4, true, "bye"]);
w.postMessage({a: 1, b: 2.5, c: "three"});
// Clone ArrayBuffer
var ab1 = createArrayBuffer(16);
w.postMessage(ab1);
assertEquals(16, ab1.byteLength); // ArrayBuffer should not be neutered.
// Transfer ArrayBuffer
var ab2 = createArrayBuffer(32);
w.postMessage(ab2, [ab2]);
assertEquals(0, ab2.byteLength); // ArrayBuffer should be neutered.
assertEquals("undefined", typeof foo);
// Read a message from the worker.
assertEquals("DONE", w.getMessage());
w.terminate();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment