blob: bb7344503e72c5429fd68e3081bffc396a208df9 [file] [log] [blame]
Anna Henningsen018d6182018-06-20 15:10:061#include "debug_utils.h"
Anna Henningsene7a23672017-09-05 20:38:322#include "node_messaging.h"
3#include "node_internals.h"
4#include "node_buffer.h"
5#include "node_errors.h"
6#include "util.h"
7#include "util-inl.h"
8#include "async_wrap.h"
9#include "async_wrap-inl.h"
10
11using v8::Array;
12using v8::ArrayBuffer;
13using v8::ArrayBufferCreationMode;
14using v8::Context;
15using v8::EscapableHandleScope;
16using v8::Exception;
17using v8::Function;
18using v8::FunctionCallbackInfo;
19using v8::FunctionTemplate;
20using v8::HandleScope;
21using v8::Isolate;
22using v8::Just;
23using v8::Local;
24using v8::Maybe;
25using v8::MaybeLocal;
26using v8::Nothing;
27using v8::Object;
Anna Henningsenb0404042018-05-13 17:39:3228using v8::SharedArrayBuffer;
Anna Henningsene7a23672017-09-05 20:38:3229using v8::String;
30using v8::Value;
31using v8::ValueDeserializer;
32using v8::ValueSerializer;
33
34namespace node {
35namespace worker {
36
37Message::Message(MallocedBuffer<char>&& buffer)
38 : main_message_buf_(std::move(buffer)) {}
39
40namespace {
41
42// This is used to tell V8 how to read transferred host objects, like other
43// `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them.
44class DeserializerDelegate : public ValueDeserializer::Delegate {
45 public:
Anna Henningsen749a13b2017-10-07 21:39:0246 DeserializerDelegate(Message* m,
47 Environment* env,
Anna Henningsenb0404042018-05-13 17:39:3248 const std::vector<MessagePort*>& message_ports,
49 const std::vector<Local<SharedArrayBuffer>>&
50 shared_array_buffers)
Daniel Bevenius19dae6b2018-05-23 11:10:5351 : message_ports_(message_ports),
Anna Henningsenb0404042018-05-13 17:39:3252 shared_array_buffers_(shared_array_buffers) {}
Anna Henningsen749a13b2017-10-07 21:39:0253
54 MaybeLocal<Object> ReadHostObject(Isolate* isolate) override {
55 // Currently, only MessagePort hosts objects are supported, so identifying
56 // by the index in the message's MessagePort array is sufficient.
57 uint32_t id;
58 if (!deserializer->ReadUint32(&id))
59 return MaybeLocal<Object>();
60 CHECK_LE(id, message_ports_.size());
Anna Henningsen0df031a2017-09-01 15:03:4161 return message_ports_[id]->object(isolate);
Anna Henningsen749a13b2017-10-07 21:39:0262 };
Anna Henningsene7a23672017-09-05 20:38:3263
Anna Henningsenb0404042018-05-13 17:39:3264 MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
65 Isolate* isolate, uint32_t clone_id) override {
66 CHECK_LE(clone_id, shared_array_buffers_.size());
67 return shared_array_buffers_[clone_id];
68 }
69
Anna Henningsene7a23672017-09-05 20:38:3270 ValueDeserializer* deserializer = nullptr;
71
72 private:
Anna Henningsen749a13b2017-10-07 21:39:0273 const std::vector<MessagePort*>& message_ports_;
Anna Henningsenb0404042018-05-13 17:39:3274 const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_;
Anna Henningsene7a23672017-09-05 20:38:3275};
76
77} // anonymous namespace
78
79MaybeLocal<Value> Message::Deserialize(Environment* env,
80 Local<Context> context) {
81 EscapableHandleScope handle_scope(env->isolate());
82 Context::Scope context_scope(context);
83
Anna Henningsen749a13b2017-10-07 21:39:0284 // Create all necessary MessagePort handles.
85 std::vector<MessagePort*> ports(message_ports_.size());
86 for (uint32_t i = 0; i < message_ports_.size(); ++i) {
87 ports[i] = MessagePort::New(env,
88 context,
89 std::move(message_ports_[i]));
90 if (ports[i] == nullptr) {
91 for (MessagePort* port : ports) {
92 // This will eventually release the MessagePort object itself.
93 port->Close();
94 }
95 return MaybeLocal<Value>();
96 }
97 }
98 message_ports_.clear();
99
Anna Henningsenb0404042018-05-13 17:39:32100 std::vector<Local<SharedArrayBuffer>> shared_array_buffers;
101 // Attach all transfered SharedArrayBuffers to their new Isolate.
102 for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) {
103 Local<SharedArrayBuffer> sab;
104 if (!shared_array_buffers_[i]->GetSharedArrayBuffer(env, context)
105 .ToLocal(&sab))
106 return MaybeLocal<Value>();
107 shared_array_buffers.push_back(sab);
108 }
109 shared_array_buffers_.clear();
110
111 DeserializerDelegate delegate(this, env, ports, shared_array_buffers);
Anna Henningsene7a23672017-09-05 20:38:32112 ValueDeserializer deserializer(
113 env->isolate(),
114 reinterpret_cast<const uint8_t*>(main_message_buf_.data),
115 main_message_buf_.size,
116 &delegate);
117 delegate.deserializer = &deserializer;
118
119 // Attach all transfered ArrayBuffers to their new Isolate.
120 for (uint32_t i = 0; i < array_buffer_contents_.size(); ++i) {
121 Local<ArrayBuffer> ab =
122 ArrayBuffer::New(env->isolate(),
123 array_buffer_contents_[i].release(),
124 array_buffer_contents_[i].size,
125 ArrayBufferCreationMode::kInternalized);
126 deserializer.TransferArrayBuffer(i, ab);
127 }
128 array_buffer_contents_.clear();
129
130 if (deserializer.ReadHeader(context).IsNothing())
131 return MaybeLocal<Value>();
132 return handle_scope.Escape(
133 deserializer.ReadValue(context).FromMaybe(Local<Value>()));
134}
135
Anna Henningsenb0404042018-05-13 17:39:32136void Message::AddSharedArrayBuffer(
137 SharedArrayBufferMetadataReference reference) {
138 shared_array_buffers_.push_back(reference);
139}
140
Anna Henningsen749a13b2017-10-07 21:39:02141void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
142 message_ports_.emplace_back(std::move(data));
143}
144
Anna Henningsene7a23672017-09-05 20:38:32145namespace {
146
Timothy Guf374d6a2018-06-25 03:10:37147void ThrowDataCloneException(Environment* env, Local<String> message) {
Timothy Gu5f3bdb02018-06-25 04:48:48148 Local<Value> argv[] = {
149 message,
150 FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError")
151 };
152 Local<Value> exception;
153 Local<Function> domexception_ctor = env->domexception_function();
154 CHECK(!domexception_ctor.IsEmpty());
155 if (!domexception_ctor->NewInstance(env->context(), arraysize(argv), argv)
156 .ToLocal(&exception)) {
157 return;
158 }
159 env->isolate()->ThrowException(exception);
160}
161
Anna Henningsene7a23672017-09-05 20:38:32162// This tells V8 how to serialize objects that it does not understand
163// (e.g. C++ objects) into the output buffer, in a way that our own
164// DeserializerDelegate understands how to unpack.
165class SerializerDelegate : public ValueSerializer::Delegate {
166 public:
167 SerializerDelegate(Environment* env, Local<Context> context, Message* m)
168 : env_(env), context_(context), msg_(m) {}
169
170 void ThrowDataCloneError(Local<String> message) override {
Timothy Guf374d6a2018-06-25 03:10:37171 ThrowDataCloneException(env_, message);
Anna Henningsene7a23672017-09-05 20:38:32172 }
173
Anna Henningsen749a13b2017-10-07 21:39:02174 Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override {
175 if (env_->message_port_constructor_template()->HasInstance(object)) {
176 return WriteMessagePort(Unwrap<MessagePort>(object));
177 }
178
179 THROW_ERR_CANNOT_TRANSFER_OBJECT(env_);
180 return Nothing<bool>();
181 }
182
Anna Henningsenb0404042018-05-13 17:39:32183 Maybe<uint32_t> GetSharedArrayBufferId(
184 Isolate* isolate,
185 Local<SharedArrayBuffer> shared_array_buffer) override {
186 uint32_t i;
187 for (i = 0; i < seen_shared_array_buffers_.size(); ++i) {
188 if (seen_shared_array_buffers_[i] == shared_array_buffer)
189 return Just(i);
190 }
191
192 auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer(
193 env_,
194 context_,
195 shared_array_buffer);
196 if (!reference) {
197 return Nothing<uint32_t>();
198 }
199 seen_shared_array_buffers_.push_back(shared_array_buffer);
200 msg_->AddSharedArrayBuffer(reference);
201 return Just(i);
202 }
203
Anna Henningsen749a13b2017-10-07 21:39:02204 void Finish() {
205 // Only close the MessagePort handles and actually transfer them
206 // once we know that serialization succeeded.
207 for (MessagePort* port : ports_) {
208 port->Close();
209 msg_->AddMessagePort(port->Detach());
210 }
211 }
212
Anna Henningsene7a23672017-09-05 20:38:32213 ValueSerializer* serializer = nullptr;
214
215 private:
Anna Henningsen749a13b2017-10-07 21:39:02216 Maybe<bool> WriteMessagePort(MessagePort* port) {
217 for (uint32_t i = 0; i < ports_.size(); i++) {
218 if (ports_[i] == port) {
219 serializer->WriteUint32(i);
220 return Just(true);
221 }
222 }
223
224 THROW_ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST(env_);
225 return Nothing<bool>();
226 }
227
Anna Henningsene7a23672017-09-05 20:38:32228 Environment* env_;
229 Local<Context> context_;
230 Message* msg_;
Anna Henningsenb0404042018-05-13 17:39:32231 std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_;
Anna Henningsen749a13b2017-10-07 21:39:02232 std::vector<MessagePort*> ports_;
Anna Henningsene7a23672017-09-05 20:38:32233
234 friend class worker::Message;
235};
236
237} // anynomous namespace
238
239Maybe<bool> Message::Serialize(Environment* env,
240 Local<Context> context,
241 Local<Value> input,
Timothy Guf374d6a2018-06-25 03:10:37242 Local<Value> transfer_list_v,
243 Local<Object> source_port) {
Anna Henningsene7a23672017-09-05 20:38:32244 HandleScope handle_scope(env->isolate());
245 Context::Scope context_scope(context);
246
247 // Verify that we're not silently overwriting an existing message.
248 CHECK(main_message_buf_.is_empty());
249
250 SerializerDelegate delegate(env, context, this);
251 ValueSerializer serializer(env->isolate(), &delegate);
252 delegate.serializer = &serializer;
253
254 std::vector<Local<ArrayBuffer>> array_buffers;
255 if (transfer_list_v->IsArray()) {
256 Local<Array> transfer_list = transfer_list_v.As<Array>();
257 uint32_t length = transfer_list->Length();
258 for (uint32_t i = 0; i < length; ++i) {
259 Local<Value> entry;
260 if (!transfer_list->Get(context, i).ToLocal(&entry))
261 return Nothing<bool>();
Anna Henningsen749a13b2017-10-07 21:39:02262 // Currently, we support ArrayBuffers and MessagePorts.
Anna Henningsene7a23672017-09-05 20:38:32263 if (entry->IsArrayBuffer()) {
264 Local<ArrayBuffer> ab = entry.As<ArrayBuffer>();
265 // If we cannot render the ArrayBuffer unusable in this Isolate and
266 // take ownership of its memory, copying the buffer will have to do.
267 if (!ab->IsNeuterable() || ab->IsExternal())
268 continue;
269 // We simply use the array index in the `array_buffers` list as the
270 // ID that we write into the serialized buffer.
271 uint32_t id = array_buffers.size();
272 array_buffers.push_back(ab);
273 serializer.TransferArrayBuffer(id, ab);
274 continue;
Anna Henningsen749a13b2017-10-07 21:39:02275 } else if (env->message_port_constructor_template()
276 ->HasInstance(entry)) {
Timothy Guf374d6a2018-06-25 03:10:37277 // Check if the source MessagePort is being transferred.
278 if (!source_port.IsEmpty() && entry == source_port) {
279 ThrowDataCloneException(
280 env,
281 FIXED_ONE_BYTE_STRING(env->isolate(),
282 "Transfer list contains source port"));
283 return Nothing<bool>();
284 }
Anna Henningsen749a13b2017-10-07 21:39:02285 MessagePort* port = Unwrap<MessagePort>(entry.As<Object>());
Timothy Guf374d6a2018-06-25 03:10:37286 if (port == nullptr || port->IsDetached()) {
287 ThrowDataCloneException(
288 env,
289 FIXED_ONE_BYTE_STRING(
290 env->isolate(),
291 "MessagePort in transfer list is already detached"));
292 return Nothing<bool>();
293 }
Anna Henningsen749a13b2017-10-07 21:39:02294 delegate.ports_.push_back(port);
295 continue;
Anna Henningsene7a23672017-09-05 20:38:32296 }
297
298 THROW_ERR_INVALID_TRANSFER_OBJECT(env);
299 return Nothing<bool>();
300 }
301 }
302
303 serializer.WriteHeader();
304 if (serializer.WriteValue(context, input).IsNothing()) {
305 return Nothing<bool>();
306 }
307
308 for (Local<ArrayBuffer> ab : array_buffers) {
309 // If serialization succeeded, we want to take ownership of
310 // (a.k.a. externalize) the underlying memory region and render
311 // it inaccessible in this Isolate.
312 ArrayBuffer::Contents contents = ab->Externalize();
313 ab->Neuter();
314 array_buffer_contents_.push_back(
315 MallocedBuffer<char> { static_cast<char*>(contents.Data()),
316 contents.ByteLength() });
317 }
318
Anna Henningsen749a13b2017-10-07 21:39:02319 delegate.Finish();
320
Anna Henningsene7a23672017-09-05 20:38:32321 // The serializer gave us a buffer allocated using `malloc()`.
322 std::pair<uint8_t*, size_t> data = serializer.Release();
323 main_message_buf_ =
324 MallocedBuffer<char>(reinterpret_cast<char*>(data.first), data.second);
325 return Just(true);
326}
327
Anna Henningsen57e30152018-06-10 14:40:13328void Message::MemoryInfo(MemoryTracker* tracker) const {
329 tracker->TrackThis(this);
330 tracker->TrackField("array_buffer_contents", array_buffer_contents_);
331 tracker->TrackFieldWithSize("shared_array_buffers",
332 shared_array_buffers_.size() * sizeof(shared_array_buffers_[0]));
333 tracker->TrackField("message_ports", message_ports_);
334}
335
Anna Henningsene7a23672017-09-05 20:38:32336MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { }
337
338MessagePortData::~MessagePortData() {
339 CHECK_EQ(owner_, nullptr);
340 Disentangle();
341}
342
Anna Henningsen57e30152018-06-10 14:40:13343void MessagePortData::MemoryInfo(MemoryTracker* tracker) const {
344 Mutex::ScopedLock lock(mutex_);
345 tracker->TrackThis(this);
346 tracker->TrackField("incoming_messages", incoming_messages_);
347}
348
Anna Henningsene7a23672017-09-05 20:38:32349void MessagePortData::AddToIncomingQueue(Message&& message) {
350 // This function will be called by other threads.
351 Mutex::ScopedLock lock(mutex_);
352 incoming_messages_.emplace_back(std::move(message));
353
Anna Henningsen018d6182018-06-20 15:10:06354 if (owner_ != nullptr) {
355 Debug(owner_, "Adding message to incoming queue");
Anna Henningsene7a23672017-09-05 20:38:32356 owner_->TriggerAsync();
Anna Henningsen018d6182018-06-20 15:10:06357 }
Anna Henningsene7a23672017-09-05 20:38:32358}
359
360bool MessagePortData::IsSiblingClosed() const {
361 Mutex::ScopedLock lock(*sibling_mutex_);
362 return sibling_ == nullptr;
363}
364
365void MessagePortData::Entangle(MessagePortData* a, MessagePortData* b) {
366 CHECK_EQ(a->sibling_, nullptr);
367 CHECK_EQ(b->sibling_, nullptr);
368 a->sibling_ = b;
369 b->sibling_ = a;
370 a->sibling_mutex_ = b->sibling_mutex_;
371}
372
373void MessagePortData::PingOwnerAfterDisentanglement() {
374 Mutex::ScopedLock lock(mutex_);
375 if (owner_ != nullptr)
376 owner_->TriggerAsync();
377}
378
379void MessagePortData::Disentangle() {
380 // Grab a copy of the sibling mutex, then replace it so that each sibling
381 // has its own sibling_mutex_ now.
382 std::shared_ptr<Mutex> sibling_mutex = sibling_mutex_;
383 Mutex::ScopedLock sibling_lock(*sibling_mutex);
384 sibling_mutex_ = std::make_shared<Mutex>();
385
386 MessagePortData* sibling = sibling_;
387 if (sibling_ != nullptr) {
388 sibling_->sibling_ = nullptr;
389 sibling_ = nullptr;
390 }
391
392 // We close MessagePorts after disentanglement, so we trigger the
393 // corresponding uv_async_t to let them know that this happened.
394 PingOwnerAfterDisentanglement();
395 if (sibling != nullptr) {
396 sibling->PingOwnerAfterDisentanglement();
397 }
398}
399
400MessagePort::~MessagePort() {
401 if (data_)
402 data_->owner_ = nullptr;
403}
404
405MessagePort::MessagePort(Environment* env,
406 Local<Context> context,
407 Local<Object> wrap)
408 : HandleWrap(env,
409 wrap,
410 reinterpret_cast<uv_handle_t*>(new uv_async_t()),
411 AsyncWrap::PROVIDER_MESSAGEPORT),
412 data_(new MessagePortData(this)) {
413 auto onmessage = [](uv_async_t* handle) {
414 // Called when data has been put into the queue.
415 MessagePort* channel = static_cast<MessagePort*>(handle->data);
416 channel->OnMessage();
417 };
418 CHECK_EQ(uv_async_init(env->event_loop(),
419 async(),
420 onmessage), 0);
421 async()->data = static_cast<void*>(this);
422
423 Local<Value> fn;
424 if (!wrap->Get(context, env->oninit_string()).ToLocal(&fn))
425 return;
426
427 if (fn->IsFunction()) {
428 Local<Function> init = fn.As<Function>();
429 USE(init->Call(context, wrap, 0, nullptr));
430 }
Anna Henningsen018d6182018-06-20 15:10:06431
432 Debug(this, "Created message port");
Anna Henningsene7a23672017-09-05 20:38:32433}
434
435void MessagePort::AddToIncomingQueue(Message&& message) {
436 data_->AddToIncomingQueue(std::move(message));
437}
438
439uv_async_t* MessagePort::async() {
440 return reinterpret_cast<uv_async_t*>(GetHandle());
441}
442
Timothy Guf374d6a2018-06-25 03:10:37443bool MessagePort::IsDetached() const {
444 return data_ == nullptr || IsHandleClosing();
445}
446
Anna Henningsene7a23672017-09-05 20:38:32447void MessagePort::TriggerAsync() {
Anna Henningsen22c826f2018-06-07 12:43:45448 if (IsHandleClosing()) return;
Anna Henningsene7a23672017-09-05 20:38:32449 CHECK_EQ(uv_async_send(async()), 0);
450}
451
Anna Henningsen22c826f2018-06-07 12:43:45452void MessagePort::Close(v8::Local<v8::Value> close_callback) {
Anna Henningsen018d6182018-06-20 15:10:06453 Debug(this, "Closing message port, data set = %d", static_cast<int>(!!data_));
454
Anna Henningsen22c826f2018-06-07 12:43:45455 if (data_) {
456 // Wrap this call with accessing the mutex, so that TriggerAsync()
457 // can check IsHandleClosing() without race conditions.
458 Mutex::ScopedLock sibling_lock(data_->mutex_);
459 HandleWrap::Close(close_callback);
460 } else {
461 HandleWrap::Close(close_callback);
462 }
463}
464
Anna Henningsene7a23672017-09-05 20:38:32465void MessagePort::New(const FunctionCallbackInfo<Value>& args) {
466 Environment* env = Environment::GetCurrent(args);
467 if (!args.IsConstructCall()) {
468 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
469 return;
470 }
471
472 Local<Context> context = args.This()->CreationContext();
473 Context::Scope context_scope(context);
474
475 new MessagePort(env, context, args.This());
476}
477
478MessagePort* MessagePort::New(
479 Environment* env,
480 Local<Context> context,
481 std::unique_ptr<MessagePortData> data) {
482 Context::Scope context_scope(context);
483 Local<Function> ctor;
484 if (!GetMessagePortConstructor(env, context).ToLocal(&ctor))
485 return nullptr;
Anna Henningsene7a23672017-09-05 20:38:32486
487 // Construct a new instance, then assign the listener instance and possibly
488 // the MessagePortData to it.
489 Local<Object> instance;
490 if (!ctor->NewInstance(context).ToLocal(&instance))
491 return nullptr;
Anna Henningsend102a852018-09-23 17:26:30492 MessagePort* port = Unwrap<MessagePort>(instance);
493 CHECK_NOT_NULL(port);
Anna Henningsene7a23672017-09-05 20:38:32494 if (data) {
495 port->Detach();
496 port->data_ = std::move(data);
497 port->data_->owner_ = port;
498 // If the existing MessagePortData object had pending messages, this is
499 // the easiest way to run that queue.
500 port->TriggerAsync();
501 }
502 return port;
503}
504
505void MessagePort::OnMessage() {
Anna Henningsen018d6182018-06-20 15:10:06506 Debug(this, "Running MessagePort::OnMessage()");
Anna Henningsene7a23672017-09-05 20:38:32507 HandleScope handle_scope(env()->isolate());
Anna Henningsen0df031a2017-09-01 15:03:41508 Local<Context> context = object(env()->isolate())->CreationContext();
Anna Henningsene7a23672017-09-05 20:38:32509
510 // data_ can only ever be modified by the owner thread, so no need to lock.
511 // However, the message port may be transferred while it is processing
512 // messages, so we need to check that this handle still owns its `data_` field
513 // on every iteration.
514 while (data_) {
515 Message received;
516 {
517 // Get the head of the message queue.
518 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen0df031a2017-09-01 15:03:41519
520 if (stop_event_loop_) {
Anna Henningsen018d6182018-06-20 15:10:06521 Debug(this, "MessagePort stops loop as requested");
Anna Henningsen0df031a2017-09-01 15:03:41522 CHECK(!data_->receiving_messages_);
523 uv_stop(env()->event_loop());
524 break;
525 }
526
Anna Henningsen018d6182018-06-20 15:10:06527 Debug(this, "MessagePort has message, receiving = %d",
528 static_cast<int>(data_->receiving_messages_));
529
Anna Henningsene7a23672017-09-05 20:38:32530 if (!data_->receiving_messages_)
531 break;
532 if (data_->incoming_messages_.empty())
533 break;
534 received = std::move(data_->incoming_messages_.front());
535 data_->incoming_messages_.pop_front();
536 }
537
538 if (!env()->can_call_into_js()) {
Anna Henningsen018d6182018-06-20 15:10:06539 Debug(this, "MessagePort drains queue because !can_call_into_js()");
Anna Henningsene7a23672017-09-05 20:38:32540 // In this case there is nothing to do but to drain the current queue.
541 continue;
542 }
543
544 {
545 // Call the JS .onmessage() callback.
546 HandleScope handle_scope(env()->isolate());
547 Context::Scope context_scope(context);
548 Local<Value> args[] = {
549 received.Deserialize(env(), context).FromMaybe(Local<Value>())
550 };
551
552 if (args[0].IsEmpty() ||
Anna Henningsene7a23672017-09-05 20:38:32553 MakeCallback(env()->onmessage_string(), 1, args).IsEmpty()) {
554 // Re-schedule OnMessage() execution in case of failure.
555 if (data_)
556 TriggerAsync();
557 return;
558 }
559 }
560 }
561
562 if (data_ && data_->IsSiblingClosed()) {
563 Close();
564 }
565}
566
567bool MessagePort::IsSiblingClosed() const {
568 CHECK(data_);
569 return data_->IsSiblingClosed();
570}
571
572void MessagePort::OnClose() {
Anna Henningsen018d6182018-06-20 15:10:06573 Debug(this, "MessagePort::OnClose()");
Anna Henningsene7a23672017-09-05 20:38:32574 if (data_) {
575 data_->owner_ = nullptr;
576 data_->Disentangle();
577 }
578 data_.reset();
579 delete async();
580}
581
582std::unique_ptr<MessagePortData> MessagePort::Detach() {
583 Mutex::ScopedLock lock(data_->mutex_);
584 data_->owner_ = nullptr;
585 return std::move(data_);
586}
587
588
Timothy Guf374d6a2018-06-25 03:10:37589Maybe<bool> MessagePort::PostMessage(Environment* env,
590 Local<Value> message_v,
591 Local<Value> transfer_v) {
592 Isolate* isolate = env->isolate();
593 Local<Object> obj = object(isolate);
594 Local<Context> context = obj->CreationContext();
Anna Henningsene7a23672017-09-05 20:38:32595
Anna Henningsene7a23672017-09-05 20:38:32596 Message msg;
Timothy Guf374d6a2018-06-25 03:10:37597
598 // Per spec, we need to both check if transfer list has the source port, and
599 // serialize the input message, even if the MessagePort is closed or detached.
600
601 Maybe<bool> serialization_maybe =
602 msg.Serialize(env, context, message_v, transfer_v, obj);
603 if (data_ == nullptr) {
604 return serialization_maybe;
Anna Henningsene7a23672017-09-05 20:38:32605 }
Timothy Guf374d6a2018-06-25 03:10:37606 if (serialization_maybe.IsNothing()) {
607 return Nothing<bool>();
608 }
609
610 Mutex::ScopedLock lock(*data_->sibling_mutex_);
611 bool doomed = false;
612
613 // Check if the target port is posted to itself.
614 if (data_->sibling_ != nullptr) {
615 for (const auto& port_data : msg.message_ports()) {
616 if (data_->sibling_ == port_data.get()) {
617 doomed = true;
618 ProcessEmitWarning(env, "The target port was posted to itself, and "
619 "the communication channel was lost");
620 break;
621 }
622 }
623 }
624
625 if (data_->sibling_ == nullptr || doomed)
626 return Just(true);
627
628 data_->sibling_->AddToIncomingQueue(std::move(msg));
629 return Just(true);
Anna Henningsene7a23672017-09-05 20:38:32630}
631
632void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
633 Environment* env = Environment::GetCurrent(args);
Anna Henningsene7a23672017-09-05 20:38:32634 if (args.Length() == 0) {
635 return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to "
636 "MessagePort.postMessage");
637 }
Timothy Guf374d6a2018-06-25 03:10:37638
639 MessagePort* port = Unwrap<MessagePort>(args.This());
640 // Even if the backing MessagePort object has already been deleted, we still
641 // want to serialize the message to ensure spec-compliant behavior w.r.t.
642 // transfers.
643 if (port == nullptr) {
644 Message msg;
645 Local<Object> obj = args.This();
646 Local<Context> context = obj->CreationContext();
647 USE(msg.Serialize(env, context, args[0], args[1], obj));
648 return;
649 }
650
651 port->PostMessage(env, args[0], args[1]);
Anna Henningsene7a23672017-09-05 20:38:32652}
653
654void MessagePort::Start() {
655 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen018d6182018-06-20 15:10:06656 Debug(this, "Start receiving messages");
Anna Henningsene7a23672017-09-05 20:38:32657 data_->receiving_messages_ = true;
658 if (!data_->incoming_messages_.empty())
659 TriggerAsync();
660}
661
662void MessagePort::Stop() {
663 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen018d6182018-06-20 15:10:06664 Debug(this, "Stop receiving messages");
Anna Henningsene7a23672017-09-05 20:38:32665 data_->receiving_messages_ = false;
666}
667
Anna Henningsen0df031a2017-09-01 15:03:41668void MessagePort::StopEventLoop() {
669 Mutex::ScopedLock lock(data_->mutex_);
670 data_->receiving_messages_ = false;
671 stop_event_loop_ = true;
672
Anna Henningsen018d6182018-06-20 15:10:06673 Debug(this, "Received StopEventLoop request");
Anna Henningsen0df031a2017-09-01 15:03:41674 TriggerAsync();
675}
676
Anna Henningsene7a23672017-09-05 20:38:32677void MessagePort::Start(const FunctionCallbackInfo<Value>& args) {
678 Environment* env = Environment::GetCurrent(args);
679 MessagePort* port;
680 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
681 if (!port->data_) {
682 THROW_ERR_CLOSED_MESSAGE_PORT(env);
683 return;
684 }
685 port->Start();
686}
687
688void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) {
689 Environment* env = Environment::GetCurrent(args);
690 MessagePort* port;
691 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
692 if (!port->data_) {
693 THROW_ERR_CLOSED_MESSAGE_PORT(env);
694 return;
695 }
696 port->Stop();
697}
698
Anna Henningsen0df031a2017-09-01 15:03:41699void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) {
700 MessagePort* port;
701 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
702 port->OnMessage();
703}
704
Anna Henningsene7a23672017-09-05 20:38:32705void MessagePort::Entangle(MessagePort* a, MessagePort* b) {
706 Entangle(a, b->data_.get());
707}
708
709void MessagePort::Entangle(MessagePort* a, MessagePortData* b) {
710 MessagePortData::Entangle(a->data_.get(), b);
711}
712
713MaybeLocal<Function> GetMessagePortConstructor(
714 Environment* env, Local<Context> context) {
715 // Factor generating the MessagePort JS constructor into its own piece
716 // of code, because it is needed early on in the child environment setup.
717 Local<FunctionTemplate> templ = env->message_port_constructor_template();
718 if (!templ.IsEmpty())
719 return templ->GetFunction(context);
720
721 {
722 Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New);
723 m->SetClassName(env->message_port_constructor_string());
724 m->InstanceTemplate()->SetInternalFieldCount(1);
725
726 AsyncWrap::AddWrapMethods(env, m);
Jon Moss45732c72018-07-11 17:53:05727 HandleWrap::AddWrapMethods(env, m);
Anna Henningsene7a23672017-09-05 20:38:32728
729 env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage);
730 env->SetProtoMethod(m, "start", MessagePort::Start);
731 env->SetProtoMethod(m, "stop", MessagePort::Stop);
Anna Henningsen0df031a2017-09-01 15:03:41732 env->SetProtoMethod(m, "drain", MessagePort::Drain);
Anna Henningsene7a23672017-09-05 20:38:32733
734 env->set_message_port_constructor_template(m);
735 }
736
737 return GetMessagePortConstructor(env, context);
738}
739
740namespace {
741
742static void MessageChannel(const FunctionCallbackInfo<Value>& args) {
743 Environment* env = Environment::GetCurrent(args);
744 if (!args.IsConstructCall()) {
745 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
746 return;
747 }
748
749 Local<Context> context = args.This()->CreationContext();
750 Context::Scope context_scope(context);
751
752 MessagePort* port1 = MessagePort::New(env, context);
753 MessagePort* port2 = MessagePort::New(env, context);
754 MessagePort::Entangle(port1, port2);
755
756 args.This()->Set(env->context(), env->port1_string(), port1->object())
757 .FromJust();
758 args.This()->Set(env->context(), env->port2_string(), port2->object())
759 .FromJust();
760}
761
Timothy Gu5f3bdb02018-06-25 04:48:48762static void RegisterDOMException(const FunctionCallbackInfo<Value>& args) {
763 Environment* env = Environment::GetCurrent(args);
764 CHECK_EQ(args.Length(), 1);
765 CHECK(args[0]->IsFunction());
766 env->set_domexception_function(args[0].As<Function>());
767}
768
Anna Henningsene7a23672017-09-05 20:38:32769static void InitMessaging(Local<Object> target,
770 Local<Value> unused,
771 Local<Context> context,
772 void* priv) {
773 Environment* env = Environment::GetCurrent(context);
774
775 {
776 Local<String> message_channel_string =
777 FIXED_ONE_BYTE_STRING(env->isolate(), "MessageChannel");
778 Local<FunctionTemplate> templ = env->NewFunctionTemplate(MessageChannel);
779 templ->SetClassName(message_channel_string);
780 target->Set(env->context(),
781 message_channel_string,
782 templ->GetFunction(context).ToLocalChecked()).FromJust();
783 }
784
785 target->Set(context,
786 env->message_port_constructor_string(),
787 GetMessagePortConstructor(env, context).ToLocalChecked())
788 .FromJust();
Timothy Gu5f3bdb02018-06-25 04:48:48789
790 env->SetMethod(target, "registerDOMException", RegisterDOMException);
Anna Henningsene7a23672017-09-05 20:38:32791}
792
793} // anonymous namespace
794
795} // namespace worker
796} // namespace node
797
798NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging)