blob: c6e701c7d94426d323a070d5547694472d9c4687 [file] [log] [blame]
Anna Henningsene7a23672017-09-05 20:38:321#include "node_messaging.h"
2#include "node_internals.h"
3#include "node_buffer.h"
4#include "node_errors.h"
5#include "util.h"
6#include "util-inl.h"
7#include "async_wrap.h"
8#include "async_wrap-inl.h"
9
10using v8::Array;
11using v8::ArrayBuffer;
12using v8::ArrayBufferCreationMode;
13using v8::Context;
14using v8::EscapableHandleScope;
15using v8::Exception;
16using v8::Function;
17using v8::FunctionCallbackInfo;
18using v8::FunctionTemplate;
19using v8::HandleScope;
20using v8::Isolate;
21using v8::Just;
22using v8::Local;
23using v8::Maybe;
24using v8::MaybeLocal;
25using v8::Nothing;
26using v8::Object;
27using v8::String;
28using v8::Value;
29using v8::ValueDeserializer;
30using v8::ValueSerializer;
31
32namespace node {
33namespace worker {
34
35Message::Message(MallocedBuffer<char>&& buffer)
36 : main_message_buf_(std::move(buffer)) {}
37
38namespace {
39
40// This is used to tell V8 how to read transferred host objects, like other
41// `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them.
42class DeserializerDelegate : public ValueDeserializer::Delegate {
43 public:
44 DeserializerDelegate(Message* m, Environment* env)
45 : env_(env), msg_(m) {}
46
47 ValueDeserializer* deserializer = nullptr;
48
49 private:
50 Environment* env_;
51 Message* msg_;
52};
53
54} // anonymous namespace
55
56MaybeLocal<Value> Message::Deserialize(Environment* env,
57 Local<Context> context) {
58 EscapableHandleScope handle_scope(env->isolate());
59 Context::Scope context_scope(context);
60
61 DeserializerDelegate delegate(this, env);
62 ValueDeserializer deserializer(
63 env->isolate(),
64 reinterpret_cast<const uint8_t*>(main_message_buf_.data),
65 main_message_buf_.size,
66 &delegate);
67 delegate.deserializer = &deserializer;
68
69 // Attach all transfered ArrayBuffers to their new Isolate.
70 for (uint32_t i = 0; i < array_buffer_contents_.size(); ++i) {
71 Local<ArrayBuffer> ab =
72 ArrayBuffer::New(env->isolate(),
73 array_buffer_contents_[i].release(),
74 array_buffer_contents_[i].size,
75 ArrayBufferCreationMode::kInternalized);
76 deserializer.TransferArrayBuffer(i, ab);
77 }
78 array_buffer_contents_.clear();
79
80 if (deserializer.ReadHeader(context).IsNothing())
81 return MaybeLocal<Value>();
82 return handle_scope.Escape(
83 deserializer.ReadValue(context).FromMaybe(Local<Value>()));
84}
85
86namespace {
87
88// This tells V8 how to serialize objects that it does not understand
89// (e.g. C++ objects) into the output buffer, in a way that our own
90// DeserializerDelegate understands how to unpack.
91class SerializerDelegate : public ValueSerializer::Delegate {
92 public:
93 SerializerDelegate(Environment* env, Local<Context> context, Message* m)
94 : env_(env), context_(context), msg_(m) {}
95
96 void ThrowDataCloneError(Local<String> message) override {
97 env_->isolate()->ThrowException(Exception::Error(message));
98 }
99
100 ValueSerializer* serializer = nullptr;
101
102 private:
103 Environment* env_;
104 Local<Context> context_;
105 Message* msg_;
106
107 friend class worker::Message;
108};
109
110} // anynomous namespace
111
112Maybe<bool> Message::Serialize(Environment* env,
113 Local<Context> context,
114 Local<Value> input,
115 Local<Value> transfer_list_v) {
116 HandleScope handle_scope(env->isolate());
117 Context::Scope context_scope(context);
118
119 // Verify that we're not silently overwriting an existing message.
120 CHECK(main_message_buf_.is_empty());
121
122 SerializerDelegate delegate(env, context, this);
123 ValueSerializer serializer(env->isolate(), &delegate);
124 delegate.serializer = &serializer;
125
126 std::vector<Local<ArrayBuffer>> array_buffers;
127 if (transfer_list_v->IsArray()) {
128 Local<Array> transfer_list = transfer_list_v.As<Array>();
129 uint32_t length = transfer_list->Length();
130 for (uint32_t i = 0; i < length; ++i) {
131 Local<Value> entry;
132 if (!transfer_list->Get(context, i).ToLocal(&entry))
133 return Nothing<bool>();
134 // Currently, we support ArrayBuffers.
135 if (entry->IsArrayBuffer()) {
136 Local<ArrayBuffer> ab = entry.As<ArrayBuffer>();
137 // If we cannot render the ArrayBuffer unusable in this Isolate and
138 // take ownership of its memory, copying the buffer will have to do.
139 if (!ab->IsNeuterable() || ab->IsExternal())
140 continue;
141 // We simply use the array index in the `array_buffers` list as the
142 // ID that we write into the serialized buffer.
143 uint32_t id = array_buffers.size();
144 array_buffers.push_back(ab);
145 serializer.TransferArrayBuffer(id, ab);
146 continue;
147 }
148
149 THROW_ERR_INVALID_TRANSFER_OBJECT(env);
150 return Nothing<bool>();
151 }
152 }
153
154 serializer.WriteHeader();
155 if (serializer.WriteValue(context, input).IsNothing()) {
156 return Nothing<bool>();
157 }
158
159 for (Local<ArrayBuffer> ab : array_buffers) {
160 // If serialization succeeded, we want to take ownership of
161 // (a.k.a. externalize) the underlying memory region and render
162 // it inaccessible in this Isolate.
163 ArrayBuffer::Contents contents = ab->Externalize();
164 ab->Neuter();
165 array_buffer_contents_.push_back(
166 MallocedBuffer<char> { static_cast<char*>(contents.Data()),
167 contents.ByteLength() });
168 }
169
170 // The serializer gave us a buffer allocated using `malloc()`.
171 std::pair<uint8_t*, size_t> data = serializer.Release();
172 main_message_buf_ =
173 MallocedBuffer<char>(reinterpret_cast<char*>(data.first), data.second);
174 return Just(true);
175}
176
177MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { }
178
179MessagePortData::~MessagePortData() {
180 CHECK_EQ(owner_, nullptr);
181 Disentangle();
182}
183
184void MessagePortData::AddToIncomingQueue(Message&& message) {
185 // This function will be called by other threads.
186 Mutex::ScopedLock lock(mutex_);
187 incoming_messages_.emplace_back(std::move(message));
188
189 if (owner_ != nullptr)
190 owner_->TriggerAsync();
191}
192
193bool MessagePortData::IsSiblingClosed() const {
194 Mutex::ScopedLock lock(*sibling_mutex_);
195 return sibling_ == nullptr;
196}
197
198void MessagePortData::Entangle(MessagePortData* a, MessagePortData* b) {
199 CHECK_EQ(a->sibling_, nullptr);
200 CHECK_EQ(b->sibling_, nullptr);
201 a->sibling_ = b;
202 b->sibling_ = a;
203 a->sibling_mutex_ = b->sibling_mutex_;
204}
205
206void MessagePortData::PingOwnerAfterDisentanglement() {
207 Mutex::ScopedLock lock(mutex_);
208 if (owner_ != nullptr)
209 owner_->TriggerAsync();
210}
211
212void MessagePortData::Disentangle() {
213 // Grab a copy of the sibling mutex, then replace it so that each sibling
214 // has its own sibling_mutex_ now.
215 std::shared_ptr<Mutex> sibling_mutex = sibling_mutex_;
216 Mutex::ScopedLock sibling_lock(*sibling_mutex);
217 sibling_mutex_ = std::make_shared<Mutex>();
218
219 MessagePortData* sibling = sibling_;
220 if (sibling_ != nullptr) {
221 sibling_->sibling_ = nullptr;
222 sibling_ = nullptr;
223 }
224
225 // We close MessagePorts after disentanglement, so we trigger the
226 // corresponding uv_async_t to let them know that this happened.
227 PingOwnerAfterDisentanglement();
228 if (sibling != nullptr) {
229 sibling->PingOwnerAfterDisentanglement();
230 }
231}
232
233MessagePort::~MessagePort() {
234 if (data_)
235 data_->owner_ = nullptr;
236}
237
238MessagePort::MessagePort(Environment* env,
239 Local<Context> context,
240 Local<Object> wrap)
241 : HandleWrap(env,
242 wrap,
243 reinterpret_cast<uv_handle_t*>(new uv_async_t()),
244 AsyncWrap::PROVIDER_MESSAGEPORT),
245 data_(new MessagePortData(this)) {
246 auto onmessage = [](uv_async_t* handle) {
247 // Called when data has been put into the queue.
248 MessagePort* channel = static_cast<MessagePort*>(handle->data);
249 channel->OnMessage();
250 };
251 CHECK_EQ(uv_async_init(env->event_loop(),
252 async(),
253 onmessage), 0);
254 async()->data = static_cast<void*>(this);
255
256 Local<Value> fn;
257 if (!wrap->Get(context, env->oninit_string()).ToLocal(&fn))
258 return;
259
260 if (fn->IsFunction()) {
261 Local<Function> init = fn.As<Function>();
262 USE(init->Call(context, wrap, 0, nullptr));
263 }
264}
265
266void MessagePort::AddToIncomingQueue(Message&& message) {
267 data_->AddToIncomingQueue(std::move(message));
268}
269
270uv_async_t* MessagePort::async() {
271 return reinterpret_cast<uv_async_t*>(GetHandle());
272}
273
274void MessagePort::TriggerAsync() {
275 CHECK_EQ(uv_async_send(async()), 0);
276}
277
278void MessagePort::New(const FunctionCallbackInfo<Value>& args) {
279 Environment* env = Environment::GetCurrent(args);
280 if (!args.IsConstructCall()) {
281 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
282 return;
283 }
284
285 Local<Context> context = args.This()->CreationContext();
286 Context::Scope context_scope(context);
287
288 new MessagePort(env, context, args.This());
289}
290
291MessagePort* MessagePort::New(
292 Environment* env,
293 Local<Context> context,
294 std::unique_ptr<MessagePortData> data) {
295 Context::Scope context_scope(context);
296 Local<Function> ctor;
297 if (!GetMessagePortConstructor(env, context).ToLocal(&ctor))
298 return nullptr;
299 MessagePort* port = nullptr;
300
301 // Construct a new instance, then assign the listener instance and possibly
302 // the MessagePortData to it.
303 Local<Object> instance;
304 if (!ctor->NewInstance(context).ToLocal(&instance))
305 return nullptr;
306 ASSIGN_OR_RETURN_UNWRAP(&port, instance, nullptr);
307 if (data) {
308 port->Detach();
309 port->data_ = std::move(data);
310 port->data_->owner_ = port;
311 // If the existing MessagePortData object had pending messages, this is
312 // the easiest way to run that queue.
313 port->TriggerAsync();
314 }
315 return port;
316}
317
318void MessagePort::OnMessage() {
319 HandleScope handle_scope(env()->isolate());
320 Local<Context> context = object()->CreationContext();
321
322 // data_ can only ever be modified by the owner thread, so no need to lock.
323 // However, the message port may be transferred while it is processing
324 // messages, so we need to check that this handle still owns its `data_` field
325 // on every iteration.
326 while (data_) {
327 Message received;
328 {
329 // Get the head of the message queue.
330 Mutex::ScopedLock lock(data_->mutex_);
331 if (!data_->receiving_messages_)
332 break;
333 if (data_->incoming_messages_.empty())
334 break;
335 received = std::move(data_->incoming_messages_.front());
336 data_->incoming_messages_.pop_front();
337 }
338
339 if (!env()->can_call_into_js()) {
340 // In this case there is nothing to do but to drain the current queue.
341 continue;
342 }
343
344 {
345 // Call the JS .onmessage() callback.
346 HandleScope handle_scope(env()->isolate());
347 Context::Scope context_scope(context);
348 Local<Value> args[] = {
349 received.Deserialize(env(), context).FromMaybe(Local<Value>())
350 };
351
352 if (args[0].IsEmpty() ||
353 !object()->Has(context, env()->onmessage_string()).FromMaybe(false) ||
354 MakeCallback(env()->onmessage_string(), 1, args).IsEmpty()) {
355 // Re-schedule OnMessage() execution in case of failure.
356 if (data_)
357 TriggerAsync();
358 return;
359 }
360 }
361 }
362
363 if (data_ && data_->IsSiblingClosed()) {
364 Close();
365 }
366}
367
368bool MessagePort::IsSiblingClosed() const {
369 CHECK(data_);
370 return data_->IsSiblingClosed();
371}
372
373void MessagePort::OnClose() {
374 if (data_) {
375 data_->owner_ = nullptr;
376 data_->Disentangle();
377 }
378 data_.reset();
379 delete async();
380}
381
382std::unique_ptr<MessagePortData> MessagePort::Detach() {
383 Mutex::ScopedLock lock(data_->mutex_);
384 data_->owner_ = nullptr;
385 return std::move(data_);
386}
387
388
389void MessagePort::Send(Message&& message) {
390 Mutex::ScopedLock lock(*data_->sibling_mutex_);
391 if (data_->sibling_ == nullptr)
392 return;
393 data_->sibling_->AddToIncomingQueue(std::move(message));
394}
395
396void MessagePort::Send(const FunctionCallbackInfo<Value>& args) {
397 Environment* env = Environment::GetCurrent(args);
398 Message msg;
399 if (msg.Serialize(env, object()->CreationContext(), args[0], args[1])
400 .IsNothing()) {
401 return;
402 }
403 Send(std::move(msg));
404}
405
406void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
407 Environment* env = Environment::GetCurrent(args);
408 MessagePort* port;
409 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
410 if (!port->data_) {
411 return THROW_ERR_CLOSED_MESSAGE_PORT(env);
412 }
413 if (args.Length() == 0) {
414 return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to "
415 "MessagePort.postMessage");
416 }
417 port->Send(args);
418}
419
420void MessagePort::Start() {
421 Mutex::ScopedLock lock(data_->mutex_);
422 data_->receiving_messages_ = true;
423 if (!data_->incoming_messages_.empty())
424 TriggerAsync();
425}
426
427void MessagePort::Stop() {
428 Mutex::ScopedLock lock(data_->mutex_);
429 data_->receiving_messages_ = false;
430}
431
432void MessagePort::Start(const FunctionCallbackInfo<Value>& args) {
433 Environment* env = Environment::GetCurrent(args);
434 MessagePort* port;
435 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
436 if (!port->data_) {
437 THROW_ERR_CLOSED_MESSAGE_PORT(env);
438 return;
439 }
440 port->Start();
441}
442
443void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) {
444 Environment* env = Environment::GetCurrent(args);
445 MessagePort* port;
446 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
447 if (!port->data_) {
448 THROW_ERR_CLOSED_MESSAGE_PORT(env);
449 return;
450 }
451 port->Stop();
452}
453
454size_t MessagePort::self_size() const {
455 Mutex::ScopedLock lock(data_->mutex_);
456 size_t sz = sizeof(*this) + sizeof(*data_);
457 for (const Message& msg : data_->incoming_messages_)
458 sz += sizeof(msg) + msg.main_message_buf_.size;
459 return sz;
460}
461
462void MessagePort::Entangle(MessagePort* a, MessagePort* b) {
463 Entangle(a, b->data_.get());
464}
465
466void MessagePort::Entangle(MessagePort* a, MessagePortData* b) {
467 MessagePortData::Entangle(a->data_.get(), b);
468}
469
470MaybeLocal<Function> GetMessagePortConstructor(
471 Environment* env, Local<Context> context) {
472 // Factor generating the MessagePort JS constructor into its own piece
473 // of code, because it is needed early on in the child environment setup.
474 Local<FunctionTemplate> templ = env->message_port_constructor_template();
475 if (!templ.IsEmpty())
476 return templ->GetFunction(context);
477
478 {
479 Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New);
480 m->SetClassName(env->message_port_constructor_string());
481 m->InstanceTemplate()->SetInternalFieldCount(1);
482
483 AsyncWrap::AddWrapMethods(env, m);
484
485 env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage);
486 env->SetProtoMethod(m, "start", MessagePort::Start);
487 env->SetProtoMethod(m, "stop", MessagePort::Stop);
488 env->SetProtoMethod(m, "close", HandleWrap::Close);
489 env->SetProtoMethod(m, "unref", HandleWrap::Unref);
490 env->SetProtoMethod(m, "ref", HandleWrap::Ref);
491 env->SetProtoMethod(m, "hasRef", HandleWrap::HasRef);
492
493 env->set_message_port_constructor_template(m);
494 }
495
496 return GetMessagePortConstructor(env, context);
497}
498
499namespace {
500
501static void MessageChannel(const FunctionCallbackInfo<Value>& args) {
502 Environment* env = Environment::GetCurrent(args);
503 if (!args.IsConstructCall()) {
504 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
505 return;
506 }
507
508 Local<Context> context = args.This()->CreationContext();
509 Context::Scope context_scope(context);
510
511 MessagePort* port1 = MessagePort::New(env, context);
512 MessagePort* port2 = MessagePort::New(env, context);
513 MessagePort::Entangle(port1, port2);
514
515 args.This()->Set(env->context(), env->port1_string(), port1->object())
516 .FromJust();
517 args.This()->Set(env->context(), env->port2_string(), port2->object())
518 .FromJust();
519}
520
521static void InitMessaging(Local<Object> target,
522 Local<Value> unused,
523 Local<Context> context,
524 void* priv) {
525 Environment* env = Environment::GetCurrent(context);
526
527 {
528 Local<String> message_channel_string =
529 FIXED_ONE_BYTE_STRING(env->isolate(), "MessageChannel");
530 Local<FunctionTemplate> templ = env->NewFunctionTemplate(MessageChannel);
531 templ->SetClassName(message_channel_string);
532 target->Set(env->context(),
533 message_channel_string,
534 templ->GetFunction(context).ToLocalChecked()).FromJust();
535 }
536
537 target->Set(context,
538 env->message_port_constructor_string(),
539 GetMessagePortConstructor(env, context).ToLocalChecked())
540 .FromJust();
541}
542
543} // anonymous namespace
544
545} // namespace worker
546} // namespace node
547
548NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging)