1/**
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <queue>
18#include <v8.h>
19
20#include "logging.h"
21#include "js_support.h"
22#include "node_object_wrap.h"
23#include "node_util.h"
24#include "util.h"
25#include "worker.h"
26
27#include "worker_v8.h"
28
29
30//#define WORKER_V8_V8_DEBUG
31#ifdef  WORKER_V8_V8_DEBUG
32
33#define DBG(...) LOGD(__VA_ARGS__)
34
35#else
36
37#define DBG(...)
38
39#endif
40
41v8::Persistent<v8::FunctionTemplate> WorkerV8Template;
42
43class WorkerV8 : public ObjectWrap {
44  private:
45    friend class Handler;
46
47
48    struct ArgInfo {
49        v8::Persistent<v8::Object> js_this;
50        v8::Persistent<v8::Value> value;
51    };
52
53    pthread_mutex_t ai_free_list_mutex_;
54    std::queue<ArgInfo *>  ai_free_list_;
55
56    ArgInfo *ObtainArgInfo() {
57        ArgInfo *ai;
58        pthread_mutex_lock(&ai_free_list_mutex_);
59        if (ai_free_list_.size() == 0) {
60            ai = new ArgInfo();
61        } else {
62            ai = ai_free_list_.front();
63            ai_free_list_.pop();
64        }
65        pthread_mutex_unlock(&ai_free_list_mutex_);
66        return ai;
67    }
68
69    void ReleaseArgInfo(ArgInfo *ai) {
70        pthread_mutex_lock(&ai_free_list_mutex_);
71        ai_free_list_.push(ai);
72        pthread_mutex_unlock(&ai_free_list_mutex_);
73    }
74
75    class Handler : public WorkerQueue {
76      private:
77        v8::Persistent<v8::Value> functionValue_;
78        WorkerV8 *worker_;
79
80      public:
81        Handler(WorkerV8 *worker, v8::Handle<v8::Value> value)
82            : worker_(worker) {
83            functionValue_ = v8::Persistent<v8::Value>::New(value);
84        }
85
86        void Process(void *param) {
87            DBG("Handler::Process: E");
88
89            v8::Locker locker;
90            v8::HandleScope handle_scope;
91            v8::TryCatch try_catch;
92            try_catch.SetVerbose(true);
93
94            ArgInfo *ai = (ArgInfo*)param;
95            v8::Handle<v8::Value> args(ai->value);
96            v8::Function::Cast(*functionValue_)->Call(ai->js_this, 1, &args);
97
98            ai->js_this.Dispose();
99            ai->value.Dispose();
100
101            worker_->ReleaseArgInfo(ai);
102
103            DBG("Handler::Process: X");
104        }
105    };
106
107    Handler *handler_;
108
109  public:
110    WorkerV8(v8::Handle<v8::Object> self, v8::Handle<v8::Value> functionValue) {
111        DBG("WorkerV8::WorkerV8 E:");
112        pthread_mutex_init(&ai_free_list_mutex_, NULL);
113        handler_ = new Handler(this, functionValue);
114        Wrap(self);
115        DBG("WorkerV8::WorkerV8 X: this=%p handler_=%p", this, handler_);
116    }
117
118    virtual ~WorkerV8() {
119        DBG("~WorkerV8::WorkerV8 E:");
120        DBG("~WorkerV8::WorkerV8 X:");
121    }
122
123    static v8::Handle<v8::Value> Run(const v8::Arguments& args) {
124        WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
125        DBG("WorkerV8::Run(args) E:");
126        workerV8->handler_->Run();
127        DBG("WorkerV8::Run(args) X:");
128        return v8::Undefined();
129    }
130
131    static v8::Handle<v8::Value> Add(const v8::Arguments& args) {
132        DBG("WorkerV8::Add(args) E:");
133        WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
134
135        // Validate one argument to add
136        if (args.Length() != 1) {
137            DBG("WorkerV8::Add(args) X: expecting one param");
138            return v8::ThrowException(v8::String::New("Add has no parameter"));
139        }
140        ArgInfo *ai = workerV8->ObtainArgInfo();
141        ai->js_this = v8::Persistent<v8::Object>::New( args.This() );
142        ai->value = v8::Persistent<v8::Value>::New( args[0] );
143
144        workerV8->handler_->Add(ai);
145        DBG("WorkerV8::Add(args) X:");
146        return v8::Undefined();
147    }
148
149    static v8::Handle<v8::Value> AddDelayed(const v8::Arguments& args) {
150        DBG("WorkerV8::AddDelayed(args) E:");
151        WorkerV8 *workerV8 = ObjectWrap::Unwrap<WorkerV8>(args.This());
152
153        // Validate two argument to addDelayed
154        if (args.Length() != 2) {
155            DBG("WorkerV8::AddDelayed(args) X: expecting two params");
156            return v8::ThrowException(v8::String::New("AddDelayed expects req delayTime params"));
157        }
158        ArgInfo *ai = workerV8->ObtainArgInfo();
159        ai->js_this = v8::Persistent<v8::Object>::New( args.This() );
160        ai->value = v8::Persistent<v8::Value>::New( args[0] );
161        v8::Handle<v8::Value> v8DelayMs(args[1]->ToObject());
162        int32_t delay_ms = v8DelayMs->Int32Value();
163        workerV8->handler_->AddDelayed(ai, delay_ms);
164
165        DBG("WorkerV8::AddDelayed(args) X:");
166        return v8::Undefined();
167    }
168
169    static v8::Handle<v8::Value> NewWorkerV8(const v8::Arguments& args) {
170        DBG("WorkerV8::NewWorkerV8 E: args.Length()=%d", args.Length());
171        WorkerV8 *worker = new WorkerV8(args.This(), args[0]);
172        DBG("WorkerV8::NewWorkerV8 X:");
173        return worker->handle_;
174    }
175};
176
177void WorkerV8Init() {
178    DBG("WorkerV8Init E:");
179    v8::HandleScope handle_scope;
180
181    WorkerV8Template = v8::Persistent<v8::FunctionTemplate>::New(
182                           v8::FunctionTemplate::New(WorkerV8::NewWorkerV8));
183    WorkerV8Template->SetClassName(v8::String::New("Worker"));
184    // native self (Field 0 is handle_) field count is at least 1
185    WorkerV8Template->InstanceTemplate()->SetInternalFieldCount(1);
186
187    // Set prototype methods
188    SET_PROTOTYPE_METHOD(WorkerV8Template, "run", WorkerV8::Run);
189    SET_PROTOTYPE_METHOD(WorkerV8Template, "add", WorkerV8::Add);
190    SET_PROTOTYPE_METHOD(WorkerV8Template, "addDelayed", WorkerV8::AddDelayed);
191
192    DBG("WorkerV8Init X:");
193}
194
195void testWorkerV8(v8::Handle<v8::Context> context) {
196    LOGD("testWorkerV8 E: ********");
197    v8::HandleScope handle_scope;
198
199    v8::TryCatch try_catch;
200    try_catch.SetVerbose(true);
201
202    LOGD("testWorkerV8 runJs");
203    runJs(context, &try_catch, "local-string",
204        "var w1 = new Worker(function (msg) {"
205        "     print('w1: ' + msg);\n"
206        "});\n"
207        "w1.run();\n"
208        "var w2 = new Worker(function (msg) {"
209        "     print('w2: ' + msg);\n"
210        "});\n"
211        "w2.run();\n"
212        "w2.addDelayed('three', 1000);\n"
213        "w2.add('one');\n"
214        "w1.add('two');\n"
215        "w1.addDelayed('four', 2000);\n"
216    );
217    LOGD("testWorkerV8 X: ********");
218}
219
220extern void WorkerV8ObjectTemplateInit(v8::Handle<v8::ObjectTemplate> target) {
221    DBG("WorkerV8ObjectTemplateInit(target) E:");
222    target->Set(v8::String::New("Worker"), WorkerV8Template);
223    DBG("WorkerV8ObjectTemplateInit(target) X:\n");
224}
225