/**
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <queue>
#include <v8.h>
#include "logging.h"
#include "js_support.h"
#include "node_object_wrap.h"
#include "node_util.h"
#include "util.h"
#include "worker.h"
#include "worker_v8.h"
//#define WORKER_V8_V8_DEBUG
#ifdef WORKER_V8_V8_DEBUG
#define DBG(...) ALOGD(__VA_ARGS__)
#else
#define DBG(...)
#endif
v8::Persistent<v8::FunctionTemplate> WorkerV8Template;
class WorkerV8 : public ObjectWrap {
private:
friend class Handler;
struct ArgInfo {
v8::Persistent<v8::Object> js_this;
v8::Persistent<v8::Value> value;
};
pthread_mutex_t ai_free_list_mutex_;
std::queue<ArgInfo *> ai_free_list_;
ArgInfo *ObtainArgInfo() {
ArgInfo *ai;
pthread_mutex_lock(&ai_free_list_mutex_);
if (ai_free_list_.size() == 0) {
ai = new ArgInfo();
} else {
ai = ai_free_list_.front();
ai_free_list_.pop();
}
pthread_mutex_unlock(&ai_free_list_mutex_);
return ai;
}
void ReleaseArgInfo(ArgInfo *ai) {
pthread_mutex_lock(&ai_free_list_mutex_);
ai_free_list_.push(ai);
pthread_mutex_unlock(&ai_free_list_mutex_);
}
class Handler : public WorkerQueue {
private:
v8::Persistent<v8::Value> functionValue_;
WorkerV8 *worker_;
public:
Handler(WorkerV8 *worker, v8::Handle<v8::Value> value)
: worker_(worker) {
functionValue_ = v8::Persistent<v8::Value>::New(value);
}
void Process(void *param) {
DBG("Handler::Process: E");
v8::Locker locker;
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
try_catch.SetVerbose(true);
ArgInfo *ai = (ArgInfo*)param;
v8::Handle<v8::Value> args(ai->value);
v8::Function::Cast(*functionValue_)->Call(ai->js_this, 1, &args);
ai->js_this.Dispose();
ai->value.Dispose();
worker_->ReleaseArgInfo(ai);
DBG("Handler::Process: X");
}
};
Handler *handler_;
public:
WorkerV8(v8::Handle<v8::Object> self, v8::Handle<v8::Value> functionValue) {
DBG("WorkerV8::WorkerV8 E:");
pthread_mutex_init(&ai_free_list_mutex_, NULL);
handler_ = new Handler(this, functionValue);
Wrap(self);
DBG("WorkerV8::WorkerV8 X: this=%p handler_=%p", this, handler_);
}
virtual ~WorkerV8() {
DBG("~WorkerV8::WorkerV8 E:");
DBG("~WorkerV8::WorkerV8 X:");
}
static v8::Handle<v8::Value> Run(const v8::Arguments& args) {
WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
DBG("WorkerV8::Run(args) E:");
workerV8->handler_->Run();
DBG("WorkerV8::Run(args) X:");
return v8::Undefined();
}
static v8::Handle<v8::Value> Add(const v8::Arguments& args) {
DBG("WorkerV8::Add(args) E:");
WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
// Validate one argument to add
if (args.Length() != 1) {
DBG("WorkerV8::Add(args) X: expecting one param");
return v8::ThrowException(v8::String::New("Add has no parameter"));
}
ArgInfo *ai = workerV8->ObtainArgInfo();
ai->js_this = v8::Persistent<v8::Object>::New( args.This() );
ai->value = v8::Persistent<v8::Value>::New( args[0] );
workerV8->handler_->Add(ai);
DBG("WorkerV8::Add(args) X:");
return v8::Undefined();
}
static v8::Handle<v8::Value> AddDelayed(const v8::Arguments& args) {
DBG("WorkerV8::AddDelayed(args) E:");
WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
// Validate two argument to addDelayed
if (args.Length() != 2) {
DBG("WorkerV8::AddDelayed(args) X: expecting two params");
return v8::ThrowException(v8::String::New("AddDelayed expects req delayTime params"));
}
ArgInfo *ai = workerV8->ObtainArgInfo();
ai->js_this = v8::Persistent<v8::Object>::New( args.This() );
ai->value = v8::Persistent<v8::Value>::New( args[0] );
v8::Handle<v8::Value> v8DelayMs(args[1]->ToObject());
int32_t delay_ms = v8DelayMs->Int32Value();
workerV8->handler_->AddDelayed(ai, delay_ms);
DBG("WorkerV8::AddDelayed(args) X:");
return v8::Undefined();
}
static v8::Handle<v8::Value> NewWorkerV8(const v8::Arguments& args) {
DBG("WorkerV8::NewWorkerV8 E: args.Length()=%d", args.Length());
WorkerV8 *worker = new WorkerV8(args.This(), args[0]);
DBG("WorkerV8::NewWorkerV8 X:");
return worker->handle_;
}
};
void WorkerV8Init() {
DBG("WorkerV8Init E:");
v8::HandleScope handle_scope;
WorkerV8Template = v8::Persistent<v8::FunctionTemplate>::New(
v8::FunctionTemplate::New(WorkerV8::NewWorkerV8));
WorkerV8Template->SetClassName(v8::String::New("Worker"));
// native self (Field 0 is handle_) field count is at least 1
WorkerV8Template->InstanceTemplate()->SetInternalFieldCount(1);
// Set prototype methods
SET_PROTOTYPE_METHOD(WorkerV8Template, "run", WorkerV8::Run);
SET_PROTOTYPE_METHOD(WorkerV8Template, "add", WorkerV8::Add);
SET_PROTOTYPE_METHOD(WorkerV8Template, "addDelayed", WorkerV8::AddDelayed);
DBG("WorkerV8Init X:");
}
void testWorkerV8(v8::Handle<v8::Context> context) {
ALOGD("testWorkerV8 E: ********");
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
try_catch.SetVerbose(true);
ALOGD("testWorkerV8 runJs");
runJs(context, &try_catch, "local-string",
"var w1 = new Worker(function (msg) {"
" print('w1: ' + msg);\n"
"});\n"
"w1.run();\n"
"var w2 = new Worker(function (msg) {"
" print('w2: ' + msg);\n"
"});\n"
"w2.run();\n"
"w2.addDelayed('three', 1000);\n"
"w2.add('one');\n"
"w1.add('two');\n"
"w1.addDelayed('four', 2000);\n"
);
ALOGD("testWorkerV8 X: ********");
}
extern void WorkerV8ObjectTemplateInit(v8::Handle<v8::ObjectTemplate> target) {
DBG("WorkerV8ObjectTemplateInit(target) E:");
target->Set(v8::String::New("Worker"), WorkerV8Template);
DBG("WorkerV8ObjectTemplateInit(target) X:\n");
}