#include "TaskStub.h" #include "PacketStream.h" #include "Serialize.h" #include "Task.h" #include "TaskTryCatch.h" #include #ifdef _WIN32 #include #include #include static const int STDIN_FILENO = 0; static const int STDOUT_FILENO = 1; static const int STDERR_FILENO = 2; #else #include #endif bool TaskStub::_determinedExecutable = false; char TaskStub::_executable[1024]; void TaskStub::initialize() { if (!_determinedExecutable) { size_t size = sizeof(_executable); uv_exepath(_executable, &size); _determinedExecutable = true; } } TaskStub::TaskStub() { initialize(); std::memset(&_process, 0, sizeof(_process)); } void TaskStub::ref() { if (++_refCount == 1) { _taskObject.ClearWeak(); } } void TaskStub::release() { if (--_refCount == 0) { _taskObject.SetWeak(this, onRelease); } } TaskStub* TaskStub::createParent(Task* task, uv_file file) { v8::Isolate::Scope isolateScope(task->_isolate); v8::HandleScope scope(task->_isolate); v8::Local context = v8::Context::New(task->_isolate, 0); context->Enter(); v8::Handle parentTemplate = v8::ObjectTemplate::New(task->_isolate); parentTemplate->SetInternalFieldCount(1); v8::Handle parentObject = parentTemplate->NewInstance(); TaskStub* parentStub = new TaskStub(); parentStub->_taskObject.Reset(task->_isolate, v8::Local::New(task->_isolate, parentObject)); parentObject->SetInternalField(0, v8::External::New(task->_isolate, parentStub)); parentStub->_owner = task; parentStub->_id = Task::kParentId; if (uv_pipe_init(task->_loop, &parentStub->_stream.getStream(), 1) != 0) { std::cerr << "uv_pipe_init failed\n"; } parentStub->_stream.setOnReceive(Task::onReceivePacket, parentStub); if (uv_pipe_open(&parentStub->_stream.getStream(), file) != 0) { std::cerr << "uv_pipe_open failed\n"; } parentStub->_stream.start(); return parentStub; } void TaskStub::create(const v8::FunctionCallbackInfo& args) { Task* parent = Task::get(args.GetIsolate()); v8::HandleScope scope(args.GetIsolate()); TaskStub* stub = new TaskStub(); v8::Handle data = v8::External::New(args.GetIsolate(), stub); v8::Handle taskTemplate = v8::ObjectTemplate::New(args.GetIsolate()); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "setImports"), v8::FunctionTemplate::New(args.GetIsolate(), setImports, data)); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "getExports"), v8::FunctionTemplate::New(args.GetIsolate(), getExports, data)); taskTemplate->SetAccessor(v8::String::NewFromUtf8(args.GetIsolate(), "onExit"), getOnExit, setOnExit, data); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "activate"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::activate, data)); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "execute"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::execute, data)); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "kill"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::kill, data)); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "statistics"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::statistics, data)); taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "setRequires"), v8::FunctionTemplate::New(args.GetIsolate(), setRequires, data)); taskTemplate->SetInternalFieldCount(1); v8::Handle taskObject = taskTemplate->NewInstance(); stub->_taskObject.Reset(args.GetIsolate(), taskObject); taskObject->SetInternalField(0, v8::External::New(args.GetIsolate(), stub)); stub->_owner = parent; taskid_t id = 0; if (parent) { do { id = parent->_nextTask++; if (parent->_nextTask == Task::kParentId) { ++parent->_nextTask; } } while (parent->_children.find(id) != parent->_children.end()); parent->_children[id] = stub; } stub->_id = id; char arg1[] = "--child"; char* argv[] = { _executable, arg1, 0 }; uv_pipe_t* pipe = reinterpret_cast(&stub->_stream.getStream()); std::memset(pipe, 0, sizeof(*pipe)); if (uv_pipe_init(parent->getLoop(), pipe, 1) != 0) { std::cerr << "uv_pipe_init failed\n"; } uv_stdio_container_t io[3]; io[0].flags = static_cast(UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); io[0].data.stream = reinterpret_cast(pipe); io[1].flags = UV_INHERIT_FD; io[1].data.fd = STDOUT_FILENO; io[2].flags = UV_INHERIT_FD; io[2].data.fd = STDERR_FILENO; uv_process_options_t options = {0}; options.args = argv; options.exit_cb = onProcessExit; options.stdio = io; options.stdio_count = sizeof(io) / sizeof(*io); options.file = argv[0]; stub->_process.data = stub; int result = uv_spawn(parent->getLoop(), &stub->_process, &options); if (result == 0) { stub->_stream.setOnReceive(Task::onReceivePacket, stub); stub->_stream.start(); args.GetReturnValue().Set(taskObject); } else { std::cerr << "uv_spawn failed: " << uv_strerror(result) << "\n"; } } void TaskStub::onProcessExit(uv_process_t* process, int64_t status, int terminationSignal) { TaskStub* stub = reinterpret_cast(process->data); if (!stub->_onExit.IsEmpty()) { TaskTryCatch tryCatch(stub->_owner); v8::HandleScope scope(stub->_owner->_isolate); v8::Handle callback = v8::Local::New(stub->_owner->_isolate, stub->_onExit); v8::Handle args[2]; args[0] = v8::Integer::New(stub->_owner->_isolate, status); args[1] = v8::Integer::New(stub->_owner->_isolate, terminationSignal); callback->Call(callback, 2, &args[0]); } stub->_stream.close(); stub->_owner->_children.erase(stub->_id); uv_close(reinterpret_cast(process), 0); } void TaskStub::onRelease(const v8::WeakCallbackData& data) { } void TaskStub::getExports(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { TaskTryCatch tryCatch(stub->_owner); v8::HandleScope scope(args.GetIsolate()); promiseid_t promise = stub->_owner->allocatePromise(); Task::sendPromiseMessage(stub->_owner, stub, kGetExports, promise, v8::Undefined(args.GetIsolate())); args.GetReturnValue().Set(stub->_owner->getPromise(promise)); } } void TaskStub::setImports(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { std::vector buffer; Serialize::store(Task::get(args.GetIsolate()), buffer, args[0]); stub->_stream.send(kSetImports, &*buffer.begin(), buffer.size()); } } void TaskStub::setRequires(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { std::vector buffer; Serialize::store(Task::get(args.GetIsolate()), buffer, args[0]); stub->_stream.send(kSetRequires, &*buffer.begin(), buffer.size()); } } void TaskStub::getOnExit(v8::Local property, const v8::PropertyCallbackInfo& args) { TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner); v8::HandleScope scope(args.GetIsolate()); args.GetReturnValue().Set(v8::Local::New(args.GetIsolate(), TaskStub::get(args.Data())->_onExit)); } void TaskStub::setOnExit(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& args) { TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner); v8::HandleScope scope(args.GetIsolate()); v8::Persistent > function(args.GetIsolate(), v8::Handle::Cast(value)); TaskStub::get(args.Data())->_onExit = function; } TaskStub* TaskStub::get(v8::Handle object) { return reinterpret_cast(v8::Handle::Cast(object)->Value()); } v8::Handle TaskStub::getTaskObject() { return v8::Local::New(_owner->getIsolate(), _taskObject); } void TaskStub::activate(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { TaskTryCatch tryCatch(stub->_owner); v8::HandleScope scope(args.GetIsolate()); v8::String::Utf8Value fileName(args[0]->ToString(args.GetIsolate())); stub->_stream.send(kActivate, 0, 0); } } void TaskStub::execute(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { TaskTryCatch tryCatch(stub->_owner); v8::HandleScope scope(args.GetIsolate()); promiseid_t promise = stub->_owner->allocatePromise(); Task::sendPromiseMessage(stub->_owner, stub, kExecute, promise, args[0]); args.GetReturnValue().Set(stub->_owner->getPromise(promise)); } } void TaskStub::kill(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { uv_process_kill(&stub->_process, SIGTERM); } } void TaskStub::statistics(const v8::FunctionCallbackInfo& args) { if (TaskStub* stub = TaskStub::get(args.Data())) { TaskTryCatch tryCatch(stub->_owner); v8::HandleScope scope(args.GetIsolate()); promiseid_t promise = stub->_owner->allocatePromise(); Task::sendPromiseMessage(stub->_owner, stub, kStatistics, promise, v8::Undefined(args.GetIsolate())); args.GetReturnValue().Set(stub->_owner->getPromise(promise)); } }