blob: 896f43cd23d3726e28ef000d930cb55e8aa6351e [file] [log] [blame]
Anna Henningsene7a23672017-09-05 20:38:321#include "node_messaging.h"
Joyee Cheung78242802018-12-31 15:18:082#include "async_wrap-inl.h"
3#include "async_wrap.h"
4#include "debug_utils.h"
Anna Henningsene7a23672017-09-05 20:38:325#include "node_buffer.h"
6#include "node_errors.h"
Joyee Cheung78242802018-12-31 15:18:087#include "node_process.h"
Anna Henningsene7a23672017-09-05 20:38:328#include "util-inl.h"
Joyee Cheung78242802018-12-31 15:18:089#include "util.h"
Anna Henningsene7a23672017-09-05 20:38:3210
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;
Anna Henningsen0a549aa2019-01-02 15:33:5833using v8::WasmCompiledModule;
Anna Henningsene7a23672017-09-05 20:38:3234
35namespace node {
36namespace worker {
37
38Message::Message(MallocedBuffer<char>&& buffer)
39 : main_message_buf_(std::move(buffer)) {}
40
41namespace {
42
43// This is used to tell V8 how to read transferred host objects, like other
44// `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them.
45class DeserializerDelegate : public ValueDeserializer::Delegate {
46 public:
Anna Henningsen0a549aa2019-01-02 15:33:5847 DeserializerDelegate(
48 Message* m,
49 Environment* env,
50 const std::vector<MessagePort*>& message_ports,
51 const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers,
52 const std::vector<WasmCompiledModule::TransferrableModule>& wasm_modules)
53 : message_ports_(message_ports),
54 shared_array_buffers_(shared_array_buffers),
55 wasm_modules_(wasm_modules) {}
Anna Henningsen749a13b2017-10-07 21:39:0256
57 MaybeLocal<Object> ReadHostObject(Isolate* isolate) override {
58 // Currently, only MessagePort hosts objects are supported, so identifying
59 // by the index in the message's MessagePort array is sufficient.
60 uint32_t id;
61 if (!deserializer->ReadUint32(&id))
62 return MaybeLocal<Object>();
63 CHECK_LE(id, message_ports_.size());
Anna Henningsen0df031a2017-09-01 15:03:4164 return message_ports_[id]->object(isolate);
Anna Henningsen749a13b2017-10-07 21:39:0265 };
Anna Henningsene7a23672017-09-05 20:38:3266
Anna Henningsenb0404042018-05-13 17:39:3267 MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
68 Isolate* isolate, uint32_t clone_id) override {
69 CHECK_LE(clone_id, shared_array_buffers_.size());
70 return shared_array_buffers_[clone_id];
71 }
72
Anna Henningsen0a549aa2019-01-02 15:33:5873 MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
74 Isolate* isolate, uint32_t transfer_id) override {
75 CHECK_LE(transfer_id, wasm_modules_.size());
76 return WasmCompiledModule::FromTransferrableModule(
77 isolate, wasm_modules_[transfer_id]);
78 }
79
Anna Henningsene7a23672017-09-05 20:38:3280 ValueDeserializer* deserializer = nullptr;
81
82 private:
Anna Henningsen749a13b2017-10-07 21:39:0283 const std::vector<MessagePort*>& message_ports_;
Anna Henningsenb0404042018-05-13 17:39:3284 const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_;
Anna Henningsen0a549aa2019-01-02 15:33:5885 const std::vector<WasmCompiledModule::TransferrableModule>& wasm_modules_;
Anna Henningsene7a23672017-09-05 20:38:3286};
87
88} // anonymous namespace
89
90MaybeLocal<Value> Message::Deserialize(Environment* env,
91 Local<Context> context) {
92 EscapableHandleScope handle_scope(env->isolate());
93 Context::Scope context_scope(context);
94
Anna Henningsen749a13b2017-10-07 21:39:0295 // Create all necessary MessagePort handles.
96 std::vector<MessagePort*> ports(message_ports_.size());
97 for (uint32_t i = 0; i < message_ports_.size(); ++i) {
98 ports[i] = MessagePort::New(env,
99 context,
100 std::move(message_ports_[i]));
101 if (ports[i] == nullptr) {
102 for (MessagePort* port : ports) {
103 // This will eventually release the MessagePort object itself.
Anna Henningsene1ab4572018-12-16 13:34:37104 if (port != nullptr)
105 port->Close();
Anna Henningsen749a13b2017-10-07 21:39:02106 }
107 return MaybeLocal<Value>();
108 }
109 }
110 message_ports_.clear();
111
Anna Henningsenb0404042018-05-13 17:39:32112 std::vector<Local<SharedArrayBuffer>> shared_array_buffers;
Brandon Smith13340d42018-10-07 01:09:29113 // Attach all transferred SharedArrayBuffers to their new Isolate.
Anna Henningsenb0404042018-05-13 17:39:32114 for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) {
115 Local<SharedArrayBuffer> sab;
116 if (!shared_array_buffers_[i]->GetSharedArrayBuffer(env, context)
117 .ToLocal(&sab))
118 return MaybeLocal<Value>();
119 shared_array_buffers.push_back(sab);
120 }
121 shared_array_buffers_.clear();
122
Anna Henningsen0a549aa2019-01-02 15:33:58123 DeserializerDelegate delegate(
124 this, env, ports, shared_array_buffers, wasm_modules_);
Anna Henningsene7a23672017-09-05 20:38:32125 ValueDeserializer deserializer(
126 env->isolate(),
127 reinterpret_cast<const uint8_t*>(main_message_buf_.data),
128 main_message_buf_.size,
129 &delegate);
130 delegate.deserializer = &deserializer;
131
Brandon Smith13340d42018-10-07 01:09:29132 // Attach all transferred ArrayBuffers to their new Isolate.
Anna Henningsene7a23672017-09-05 20:38:32133 for (uint32_t i = 0; i < array_buffer_contents_.size(); ++i) {
134 Local<ArrayBuffer> ab =
135 ArrayBuffer::New(env->isolate(),
136 array_buffer_contents_[i].release(),
137 array_buffer_contents_[i].size,
138 ArrayBufferCreationMode::kInternalized);
139 deserializer.TransferArrayBuffer(i, ab);
140 }
141 array_buffer_contents_.clear();
142
143 if (deserializer.ReadHeader(context).IsNothing())
144 return MaybeLocal<Value>();
145 return handle_scope.Escape(
146 deserializer.ReadValue(context).FromMaybe(Local<Value>()));
147}
148
Anna Henningsenb0404042018-05-13 17:39:32149void Message::AddSharedArrayBuffer(
150 SharedArrayBufferMetadataReference reference) {
151 shared_array_buffers_.push_back(reference);
152}
153
Anna Henningsen749a13b2017-10-07 21:39:02154void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
155 message_ports_.emplace_back(std::move(data));
156}
157
Anna Henningsen0a549aa2019-01-02 15:33:58158uint32_t Message::AddWASMModule(WasmCompiledModule::TransferrableModule&& mod) {
159 wasm_modules_.emplace_back(std::move(mod));
160 return wasm_modules_.size() - 1;
161}
162
Anna Henningsene7a23672017-09-05 20:38:32163namespace {
164
Timothy Guf374d6a2018-06-25 03:10:37165void ThrowDataCloneException(Environment* env, Local<String> message) {
Timothy Gu5f3bdb02018-06-25 04:48:48166 Local<Value> argv[] = {
167 message,
168 FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError")
169 };
170 Local<Value> exception;
171 Local<Function> domexception_ctor = env->domexception_function();
172 CHECK(!domexception_ctor.IsEmpty());
173 if (!domexception_ctor->NewInstance(env->context(), arraysize(argv), argv)
174 .ToLocal(&exception)) {
175 return;
176 }
177 env->isolate()->ThrowException(exception);
178}
179
Anna Henningsene7a23672017-09-05 20:38:32180// This tells V8 how to serialize objects that it does not understand
181// (e.g. C++ objects) into the output buffer, in a way that our own
182// DeserializerDelegate understands how to unpack.
183class SerializerDelegate : public ValueSerializer::Delegate {
184 public:
185 SerializerDelegate(Environment* env, Local<Context> context, Message* m)
186 : env_(env), context_(context), msg_(m) {}
187
188 void ThrowDataCloneError(Local<String> message) override {
Timothy Guf374d6a2018-06-25 03:10:37189 ThrowDataCloneException(env_, message);
Anna Henningsene7a23672017-09-05 20:38:32190 }
191
Anna Henningsen749a13b2017-10-07 21:39:02192 Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override {
193 if (env_->message_port_constructor_template()->HasInstance(object)) {
194 return WriteMessagePort(Unwrap<MessagePort>(object));
195 }
196
197 THROW_ERR_CANNOT_TRANSFER_OBJECT(env_);
198 return Nothing<bool>();
199 }
200
Anna Henningsenb0404042018-05-13 17:39:32201 Maybe<uint32_t> GetSharedArrayBufferId(
202 Isolate* isolate,
203 Local<SharedArrayBuffer> shared_array_buffer) override {
204 uint32_t i;
205 for (i = 0; i < seen_shared_array_buffers_.size(); ++i) {
206 if (seen_shared_array_buffers_[i] == shared_array_buffer)
207 return Just(i);
208 }
209
210 auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer(
211 env_,
212 context_,
213 shared_array_buffer);
214 if (!reference) {
215 return Nothing<uint32_t>();
216 }
217 seen_shared_array_buffers_.push_back(shared_array_buffer);
218 msg_->AddSharedArrayBuffer(reference);
219 return Just(i);
220 }
221
Anna Henningsen0a549aa2019-01-02 15:33:58222 Maybe<uint32_t> GetWasmModuleTransferId(
223 Isolate* isolate, Local<WasmCompiledModule> module) override {
224 return Just(msg_->AddWASMModule(module->GetTransferrableModule()));
225 }
226
Anna Henningsen749a13b2017-10-07 21:39:02227 void Finish() {
228 // Only close the MessagePort handles and actually transfer them
229 // once we know that serialization succeeded.
230 for (MessagePort* port : ports_) {
231 port->Close();
232 msg_->AddMessagePort(port->Detach());
233 }
234 }
235
Anna Henningsene7a23672017-09-05 20:38:32236 ValueSerializer* serializer = nullptr;
237
238 private:
Anna Henningsen749a13b2017-10-07 21:39:02239 Maybe<bool> WriteMessagePort(MessagePort* port) {
240 for (uint32_t i = 0; i < ports_.size(); i++) {
241 if (ports_[i] == port) {
242 serializer->WriteUint32(i);
243 return Just(true);
244 }
245 }
246
247 THROW_ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST(env_);
248 return Nothing<bool>();
249 }
250
Anna Henningsene7a23672017-09-05 20:38:32251 Environment* env_;
252 Local<Context> context_;
253 Message* msg_;
Anna Henningsenb0404042018-05-13 17:39:32254 std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_;
Anna Henningsen749a13b2017-10-07 21:39:02255 std::vector<MessagePort*> ports_;
Anna Henningsene7a23672017-09-05 20:38:32256
257 friend class worker::Message;
258};
259
Brandon Smith13340d42018-10-07 01:09:29260} // anonymous namespace
Anna Henningsene7a23672017-09-05 20:38:32261
262Maybe<bool> Message::Serialize(Environment* env,
263 Local<Context> context,
264 Local<Value> input,
Timothy Guf374d6a2018-06-25 03:10:37265 Local<Value> transfer_list_v,
266 Local<Object> source_port) {
Anna Henningsene7a23672017-09-05 20:38:32267 HandleScope handle_scope(env->isolate());
268 Context::Scope context_scope(context);
269
270 // Verify that we're not silently overwriting an existing message.
271 CHECK(main_message_buf_.is_empty());
272
273 SerializerDelegate delegate(env, context, this);
274 ValueSerializer serializer(env->isolate(), &delegate);
275 delegate.serializer = &serializer;
276
277 std::vector<Local<ArrayBuffer>> array_buffers;
278 if (transfer_list_v->IsArray()) {
279 Local<Array> transfer_list = transfer_list_v.As<Array>();
280 uint32_t length = transfer_list->Length();
281 for (uint32_t i = 0; i < length; ++i) {
282 Local<Value> entry;
283 if (!transfer_list->Get(context, i).ToLocal(&entry))
284 return Nothing<bool>();
Anna Henningsen749a13b2017-10-07 21:39:02285 // Currently, we support ArrayBuffers and MessagePorts.
Anna Henningsene7a23672017-09-05 20:38:32286 if (entry->IsArrayBuffer()) {
287 Local<ArrayBuffer> ab = entry.As<ArrayBuffer>();
288 // If we cannot render the ArrayBuffer unusable in this Isolate and
289 // take ownership of its memory, copying the buffer will have to do.
290 if (!ab->IsNeuterable() || ab->IsExternal())
291 continue;
Anna Henningsen0ff0af52019-01-30 13:57:24292 if (std::find(array_buffers.begin(), array_buffers.end(), ab) !=
293 array_buffers.end()) {
294 ThrowDataCloneException(
295 env,
296 FIXED_ONE_BYTE_STRING(
297 env->isolate(),
298 "Transfer list contains duplicate ArrayBuffer"));
299 return Nothing<bool>();
300 }
Anna Henningsene7a23672017-09-05 20:38:32301 // We simply use the array index in the `array_buffers` list as the
302 // ID that we write into the serialized buffer.
303 uint32_t id = array_buffers.size();
304 array_buffers.push_back(ab);
305 serializer.TransferArrayBuffer(id, ab);
306 continue;
Anna Henningsen749a13b2017-10-07 21:39:02307 } else if (env->message_port_constructor_template()
308 ->HasInstance(entry)) {
Timothy Guf374d6a2018-06-25 03:10:37309 // Check if the source MessagePort is being transferred.
310 if (!source_port.IsEmpty() && entry == source_port) {
311 ThrowDataCloneException(
312 env,
313 FIXED_ONE_BYTE_STRING(env->isolate(),
314 "Transfer list contains source port"));
315 return Nothing<bool>();
316 }
Anna Henningsen749a13b2017-10-07 21:39:02317 MessagePort* port = Unwrap<MessagePort>(entry.As<Object>());
Timothy Guf374d6a2018-06-25 03:10:37318 if (port == nullptr || port->IsDetached()) {
319 ThrowDataCloneException(
320 env,
321 FIXED_ONE_BYTE_STRING(
322 env->isolate(),
323 "MessagePort in transfer list is already detached"));
324 return Nothing<bool>();
325 }
Anna Henningsen0ff0af52019-01-30 13:57:24326 if (std::find(delegate.ports_.begin(), delegate.ports_.end(), port) !=
327 delegate.ports_.end()) {
328 ThrowDataCloneException(
329 env,
330 FIXED_ONE_BYTE_STRING(
331 env->isolate(),
332 "Transfer list contains duplicate MessagePort"));
333 return Nothing<bool>();
334 }
Anna Henningsen749a13b2017-10-07 21:39:02335 delegate.ports_.push_back(port);
336 continue;
Anna Henningsene7a23672017-09-05 20:38:32337 }
338
339 THROW_ERR_INVALID_TRANSFER_OBJECT(env);
340 return Nothing<bool>();
341 }
342 }
343
344 serializer.WriteHeader();
345 if (serializer.WriteValue(context, input).IsNothing()) {
346 return Nothing<bool>();
347 }
348
349 for (Local<ArrayBuffer> ab : array_buffers) {
350 // If serialization succeeded, we want to take ownership of
351 // (a.k.a. externalize) the underlying memory region and render
352 // it inaccessible in this Isolate.
353 ArrayBuffer::Contents contents = ab->Externalize();
354 ab->Neuter();
355 array_buffer_contents_.push_back(
356 MallocedBuffer<char> { static_cast<char*>(contents.Data()),
357 contents.ByteLength() });
358 }
359
Anna Henningsen749a13b2017-10-07 21:39:02360 delegate.Finish();
361
Anna Henningsene7a23672017-09-05 20:38:32362 // The serializer gave us a buffer allocated using `malloc()`.
363 std::pair<uint8_t*, size_t> data = serializer.Release();
364 main_message_buf_ =
365 MallocedBuffer<char>(reinterpret_cast<char*>(data.first), data.second);
366 return Just(true);
367}
368
Anna Henningsen57e30152018-06-10 14:40:13369void Message::MemoryInfo(MemoryTracker* tracker) const {
Anna Henningsen57e30152018-06-10 14:40:13370 tracker->TrackField("array_buffer_contents", array_buffer_contents_);
371 tracker->TrackFieldWithSize("shared_array_buffers",
372 shared_array_buffers_.size() * sizeof(shared_array_buffers_[0]));
373 tracker->TrackField("message_ports", message_ports_);
374}
375
Anna Henningsene7a23672017-09-05 20:38:32376MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { }
377
378MessagePortData::~MessagePortData() {
ZYSzys93e0c6a2019-02-04 07:38:51379 CHECK_NULL(owner_);
Anna Henningsene7a23672017-09-05 20:38:32380 Disentangle();
381}
382
Anna Henningsen57e30152018-06-10 14:40:13383void MessagePortData::MemoryInfo(MemoryTracker* tracker) const {
384 Mutex::ScopedLock lock(mutex_);
Anna Henningsen57e30152018-06-10 14:40:13385 tracker->TrackField("incoming_messages", incoming_messages_);
386}
387
Anna Henningsene7a23672017-09-05 20:38:32388void MessagePortData::AddToIncomingQueue(Message&& message) {
389 // This function will be called by other threads.
390 Mutex::ScopedLock lock(mutex_);
391 incoming_messages_.emplace_back(std::move(message));
392
Anna Henningsen018d6182018-06-20 15:10:06393 if (owner_ != nullptr) {
394 Debug(owner_, "Adding message to incoming queue");
Anna Henningsene7a23672017-09-05 20:38:32395 owner_->TriggerAsync();
Anna Henningsen018d6182018-06-20 15:10:06396 }
Anna Henningsene7a23672017-09-05 20:38:32397}
398
399bool MessagePortData::IsSiblingClosed() const {
400 Mutex::ScopedLock lock(*sibling_mutex_);
401 return sibling_ == nullptr;
402}
403
404void MessagePortData::Entangle(MessagePortData* a, MessagePortData* b) {
ZYSzys93e0c6a2019-02-04 07:38:51405 CHECK_NULL(a->sibling_);
406 CHECK_NULL(b->sibling_);
Anna Henningsene7a23672017-09-05 20:38:32407 a->sibling_ = b;
408 b->sibling_ = a;
409 a->sibling_mutex_ = b->sibling_mutex_;
410}
411
412void MessagePortData::PingOwnerAfterDisentanglement() {
413 Mutex::ScopedLock lock(mutex_);
414 if (owner_ != nullptr)
415 owner_->TriggerAsync();
416}
417
418void MessagePortData::Disentangle() {
419 // Grab a copy of the sibling mutex, then replace it so that each sibling
420 // has its own sibling_mutex_ now.
421 std::shared_ptr<Mutex> sibling_mutex = sibling_mutex_;
422 Mutex::ScopedLock sibling_lock(*sibling_mutex);
423 sibling_mutex_ = std::make_shared<Mutex>();
424
425 MessagePortData* sibling = sibling_;
426 if (sibling_ != nullptr) {
427 sibling_->sibling_ = nullptr;
428 sibling_ = nullptr;
429 }
430
431 // We close MessagePorts after disentanglement, so we trigger the
432 // corresponding uv_async_t to let them know that this happened.
433 PingOwnerAfterDisentanglement();
434 if (sibling != nullptr) {
435 sibling->PingOwnerAfterDisentanglement();
436 }
437}
438
439MessagePort::~MessagePort() {
440 if (data_)
441 data_->owner_ = nullptr;
442}
443
444MessagePort::MessagePort(Environment* env,
445 Local<Context> context,
446 Local<Object> wrap)
447 : HandleWrap(env,
448 wrap,
449 reinterpret_cast<uv_handle_t*>(new uv_async_t()),
450 AsyncWrap::PROVIDER_MESSAGEPORT),
451 data_(new MessagePortData(this)) {
452 auto onmessage = [](uv_async_t* handle) {
453 // Called when data has been put into the queue.
454 MessagePort* channel = static_cast<MessagePort*>(handle->data);
455 channel->OnMessage();
456 };
457 CHECK_EQ(uv_async_init(env->event_loop(),
458 async(),
459 onmessage), 0);
460 async()->data = static_cast<void*>(this);
461
462 Local<Value> fn;
Anna Henningsen2d65e672018-09-23 17:10:54463 if (!wrap->Get(context, env->oninit_symbol()).ToLocal(&fn))
Anna Henningsene7a23672017-09-05 20:38:32464 return;
465
466 if (fn->IsFunction()) {
467 Local<Function> init = fn.As<Function>();
468 USE(init->Call(context, wrap, 0, nullptr));
469 }
Anna Henningsen018d6182018-06-20 15:10:06470
471 Debug(this, "Created message port");
Anna Henningsene7a23672017-09-05 20:38:32472}
473
474void MessagePort::AddToIncomingQueue(Message&& message) {
475 data_->AddToIncomingQueue(std::move(message));
476}
477
478uv_async_t* MessagePort::async() {
479 return reinterpret_cast<uv_async_t*>(GetHandle());
480}
481
Timothy Guf374d6a2018-06-25 03:10:37482bool MessagePort::IsDetached() const {
483 return data_ == nullptr || IsHandleClosing();
484}
485
Anna Henningsene7a23672017-09-05 20:38:32486void MessagePort::TriggerAsync() {
Anna Henningsen22c826f2018-06-07 12:43:45487 if (IsHandleClosing()) return;
Anna Henningsene7a23672017-09-05 20:38:32488 CHECK_EQ(uv_async_send(async()), 0);
489}
490
Anna Henningsen22c826f2018-06-07 12:43:45491void MessagePort::Close(v8::Local<v8::Value> close_callback) {
Anna Henningsen018d6182018-06-20 15:10:06492 Debug(this, "Closing message port, data set = %d", static_cast<int>(!!data_));
493
Anna Henningsen22c826f2018-06-07 12:43:45494 if (data_) {
495 // Wrap this call with accessing the mutex, so that TriggerAsync()
496 // can check IsHandleClosing() without race conditions.
497 Mutex::ScopedLock sibling_lock(data_->mutex_);
498 HandleWrap::Close(close_callback);
499 } else {
500 HandleWrap::Close(close_callback);
501 }
502}
503
Anna Henningsene7a23672017-09-05 20:38:32504void MessagePort::New(const FunctionCallbackInfo<Value>& args) {
505 Environment* env = Environment::GetCurrent(args);
506 if (!args.IsConstructCall()) {
507 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
508 return;
509 }
510
511 Local<Context> context = args.This()->CreationContext();
512 Context::Scope context_scope(context);
513
514 new MessagePort(env, context, args.This());
515}
516
517MessagePort* MessagePort::New(
518 Environment* env,
519 Local<Context> context,
520 std::unique_ptr<MessagePortData> data) {
521 Context::Scope context_scope(context);
522 Local<Function> ctor;
523 if (!GetMessagePortConstructor(env, context).ToLocal(&ctor))
524 return nullptr;
Anna Henningsene7a23672017-09-05 20:38:32525
526 // Construct a new instance, then assign the listener instance and possibly
527 // the MessagePortData to it.
528 Local<Object> instance;
529 if (!ctor->NewInstance(context).ToLocal(&instance))
530 return nullptr;
Anna Henningsend102a852018-09-23 17:26:30531 MessagePort* port = Unwrap<MessagePort>(instance);
532 CHECK_NOT_NULL(port);
Anna Henningsene7a23672017-09-05 20:38:32533 if (data) {
534 port->Detach();
535 port->data_ = std::move(data);
Anna Henningsenb1cc1af2019-02-03 19:20:52536
537 // This lock is here to avoid race conditions with the `owner_` read
538 // in AddToIncomingQueue(). (This would likely be unproblematic without it,
539 // but it's better to be safe than sorry.)
540 Mutex::ScopedLock lock(port->data_->mutex_);
Anna Henningsene7a23672017-09-05 20:38:32541 port->data_->owner_ = port;
542 // If the existing MessagePortData object had pending messages, this is
543 // the easiest way to run that queue.
544 port->TriggerAsync();
545 }
546 return port;
547}
548
549void MessagePort::OnMessage() {
Anna Henningsen018d6182018-06-20 15:10:06550 Debug(this, "Running MessagePort::OnMessage()");
Anna Henningsene7a23672017-09-05 20:38:32551 HandleScope handle_scope(env()->isolate());
Anna Henningsen0df031a2017-09-01 15:03:41552 Local<Context> context = object(env()->isolate())->CreationContext();
Anna Henningsene7a23672017-09-05 20:38:32553
554 // data_ can only ever be modified by the owner thread, so no need to lock.
555 // However, the message port may be transferred while it is processing
556 // messages, so we need to check that this handle still owns its `data_` field
557 // on every iteration.
558 while (data_) {
559 Message received;
560 {
561 // Get the head of the message queue.
562 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen0df031a2017-09-01 15:03:41563
564 if (stop_event_loop_) {
Anna Henningsen018d6182018-06-20 15:10:06565 Debug(this, "MessagePort stops loop as requested");
Anna Henningsen0df031a2017-09-01 15:03:41566 CHECK(!data_->receiving_messages_);
567 uv_stop(env()->event_loop());
568 break;
569 }
570
Anna Henningsen018d6182018-06-20 15:10:06571 Debug(this, "MessagePort has message, receiving = %d",
572 static_cast<int>(data_->receiving_messages_));
573
Anna Henningsene7a23672017-09-05 20:38:32574 if (!data_->receiving_messages_)
575 break;
576 if (data_->incoming_messages_.empty())
577 break;
578 received = std::move(data_->incoming_messages_.front());
579 data_->incoming_messages_.pop_front();
580 }
581
582 if (!env()->can_call_into_js()) {
Anna Henningsen018d6182018-06-20 15:10:06583 Debug(this, "MessagePort drains queue because !can_call_into_js()");
Anna Henningsene7a23672017-09-05 20:38:32584 // In this case there is nothing to do but to drain the current queue.
585 continue;
586 }
587
588 {
589 // Call the JS .onmessage() callback.
590 HandleScope handle_scope(env()->isolate());
591 Context::Scope context_scope(context);
592 Local<Value> args[] = {
593 received.Deserialize(env(), context).FromMaybe(Local<Value>())
594 };
595
596 if (args[0].IsEmpty() ||
Anna Henningsene7a23672017-09-05 20:38:32597 MakeCallback(env()->onmessage_string(), 1, args).IsEmpty()) {
598 // Re-schedule OnMessage() execution in case of failure.
599 if (data_)
600 TriggerAsync();
601 return;
602 }
603 }
604 }
605
606 if (data_ && data_->IsSiblingClosed()) {
607 Close();
608 }
609}
610
611bool MessagePort::IsSiblingClosed() const {
612 CHECK(data_);
613 return data_->IsSiblingClosed();
614}
615
616void MessagePort::OnClose() {
Anna Henningsen018d6182018-06-20 15:10:06617 Debug(this, "MessagePort::OnClose()");
Anna Henningsene7a23672017-09-05 20:38:32618 if (data_) {
619 data_->owner_ = nullptr;
620 data_->Disentangle();
621 }
622 data_.reset();
623 delete async();
624}
625
626std::unique_ptr<MessagePortData> MessagePort::Detach() {
Anna Henningsen0ff0af52019-01-30 13:57:24627 CHECK(data_);
Anna Henningsene7a23672017-09-05 20:38:32628 Mutex::ScopedLock lock(data_->mutex_);
629 data_->owner_ = nullptr;
630 return std::move(data_);
631}
632
633
Timothy Guf374d6a2018-06-25 03:10:37634Maybe<bool> MessagePort::PostMessage(Environment* env,
635 Local<Value> message_v,
636 Local<Value> transfer_v) {
637 Isolate* isolate = env->isolate();
638 Local<Object> obj = object(isolate);
639 Local<Context> context = obj->CreationContext();
Anna Henningsene7a23672017-09-05 20:38:32640
Anna Henningsene7a23672017-09-05 20:38:32641 Message msg;
Timothy Guf374d6a2018-06-25 03:10:37642
643 // Per spec, we need to both check if transfer list has the source port, and
644 // serialize the input message, even if the MessagePort is closed or detached.
645
646 Maybe<bool> serialization_maybe =
647 msg.Serialize(env, context, message_v, transfer_v, obj);
648 if (data_ == nullptr) {
649 return serialization_maybe;
Anna Henningsene7a23672017-09-05 20:38:32650 }
Timothy Guf374d6a2018-06-25 03:10:37651 if (serialization_maybe.IsNothing()) {
652 return Nothing<bool>();
653 }
654
655 Mutex::ScopedLock lock(*data_->sibling_mutex_);
656 bool doomed = false;
657
658 // Check if the target port is posted to itself.
659 if (data_->sibling_ != nullptr) {
660 for (const auto& port_data : msg.message_ports()) {
661 if (data_->sibling_ == port_data.get()) {
662 doomed = true;
663 ProcessEmitWarning(env, "The target port was posted to itself, and "
664 "the communication channel was lost");
665 break;
666 }
667 }
668 }
669
670 if (data_->sibling_ == nullptr || doomed)
671 return Just(true);
672
673 data_->sibling_->AddToIncomingQueue(std::move(msg));
674 return Just(true);
Anna Henningsene7a23672017-09-05 20:38:32675}
676
677void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
678 Environment* env = Environment::GetCurrent(args);
Anna Henningsene7a23672017-09-05 20:38:32679 if (args.Length() == 0) {
680 return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to "
681 "MessagePort.postMessage");
682 }
Timothy Guf374d6a2018-06-25 03:10:37683
684 MessagePort* port = Unwrap<MessagePort>(args.This());
685 // Even if the backing MessagePort object has already been deleted, we still
686 // want to serialize the message to ensure spec-compliant behavior w.r.t.
687 // transfers.
688 if (port == nullptr) {
689 Message msg;
690 Local<Object> obj = args.This();
691 Local<Context> context = obj->CreationContext();
692 USE(msg.Serialize(env, context, args[0], args[1], obj));
693 return;
694 }
695
696 port->PostMessage(env, args[0], args[1]);
Anna Henningsene7a23672017-09-05 20:38:32697}
698
699void MessagePort::Start() {
700 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen018d6182018-06-20 15:10:06701 Debug(this, "Start receiving messages");
Anna Henningsene7a23672017-09-05 20:38:32702 data_->receiving_messages_ = true;
703 if (!data_->incoming_messages_.empty())
704 TriggerAsync();
705}
706
707void MessagePort::Stop() {
708 Mutex::ScopedLock lock(data_->mutex_);
Anna Henningsen018d6182018-06-20 15:10:06709 Debug(this, "Stop receiving messages");
Anna Henningsene7a23672017-09-05 20:38:32710 data_->receiving_messages_ = false;
711}
712
Anna Henningsen0df031a2017-09-01 15:03:41713void MessagePort::StopEventLoop() {
714 Mutex::ScopedLock lock(data_->mutex_);
715 data_->receiving_messages_ = false;
716 stop_event_loop_ = true;
717
Anna Henningsen018d6182018-06-20 15:10:06718 Debug(this, "Received StopEventLoop request");
Anna Henningsen0df031a2017-09-01 15:03:41719 TriggerAsync();
720}
721
Anna Henningsene7a23672017-09-05 20:38:32722void MessagePort::Start(const FunctionCallbackInfo<Value>& args) {
723 Environment* env = Environment::GetCurrent(args);
724 MessagePort* port;
725 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
726 if (!port->data_) {
727 THROW_ERR_CLOSED_MESSAGE_PORT(env);
728 return;
729 }
730 port->Start();
731}
732
733void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) {
734 Environment* env = Environment::GetCurrent(args);
735 MessagePort* port;
736 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
737 if (!port->data_) {
738 THROW_ERR_CLOSED_MESSAGE_PORT(env);
739 return;
740 }
741 port->Stop();
742}
743
Anna Henningsen0df031a2017-09-01 15:03:41744void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) {
745 MessagePort* port;
746 ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
747 port->OnMessage();
748}
749
Anna Henningsene7a23672017-09-05 20:38:32750void MessagePort::Entangle(MessagePort* a, MessagePort* b) {
751 Entangle(a, b->data_.get());
752}
753
754void MessagePort::Entangle(MessagePort* a, MessagePortData* b) {
755 MessagePortData::Entangle(a->data_.get(), b);
756}
757
758MaybeLocal<Function> GetMessagePortConstructor(
759 Environment* env, Local<Context> context) {
760 // Factor generating the MessagePort JS constructor into its own piece
761 // of code, because it is needed early on in the child environment setup.
762 Local<FunctionTemplate> templ = env->message_port_constructor_template();
763 if (!templ.IsEmpty())
764 return templ->GetFunction(context);
765
766 {
767 Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New);
768 m->SetClassName(env->message_port_constructor_string());
769 m->InstanceTemplate()->SetInternalFieldCount(1);
Anna Henningsend527dde2018-09-23 17:24:33770 m->Inherit(HandleWrap::GetConstructorTemplate(env));
Anna Henningsene7a23672017-09-05 20:38:32771
772 env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage);
773 env->SetProtoMethod(m, "start", MessagePort::Start);
774 env->SetProtoMethod(m, "stop", MessagePort::Stop);
Anna Henningsen0df031a2017-09-01 15:03:41775 env->SetProtoMethod(m, "drain", MessagePort::Drain);
Anna Henningsene7a23672017-09-05 20:38:32776
777 env->set_message_port_constructor_template(m);
778 }
779
780 return GetMessagePortConstructor(env, context);
781}
782
783namespace {
784
785static void MessageChannel(const FunctionCallbackInfo<Value>& args) {
786 Environment* env = Environment::GetCurrent(args);
787 if (!args.IsConstructCall()) {
788 THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
789 return;
790 }
791
792 Local<Context> context = args.This()->CreationContext();
793 Context::Scope context_scope(context);
794
795 MessagePort* port1 = MessagePort::New(env, context);
796 MessagePort* port2 = MessagePort::New(env, context);
797 MessagePort::Entangle(port1, port2);
798
799 args.This()->Set(env->context(), env->port1_string(), port1->object())
800 .FromJust();
801 args.This()->Set(env->context(), env->port2_string(), port2->object())
802 .FromJust();
803}
804
Timothy Gu5f3bdb02018-06-25 04:48:48805static void RegisterDOMException(const FunctionCallbackInfo<Value>& args) {
806 Environment* env = Environment::GetCurrent(args);
807 CHECK_EQ(args.Length(), 1);
808 CHECK(args[0]->IsFunction());
809 env->set_domexception_function(args[0].As<Function>());
810}
811
Anna Henningsene7a23672017-09-05 20:38:32812static void InitMessaging(Local<Object> target,
813 Local<Value> unused,
814 Local<Context> context,
815 void* priv) {
816 Environment* env = Environment::GetCurrent(context);
817
818 {
819 Local<String> message_channel_string =
820 FIXED_ONE_BYTE_STRING(env->isolate(), "MessageChannel");
821 Local<FunctionTemplate> templ = env->NewFunctionTemplate(MessageChannel);
822 templ->SetClassName(message_channel_string);
823 target->Set(env->context(),
824 message_channel_string,
825 templ->GetFunction(context).ToLocalChecked()).FromJust();
826 }
827
828 target->Set(context,
829 env->message_port_constructor_string(),
830 GetMessagePortConstructor(env, context).ToLocalChecked())
831 .FromJust();
Timothy Gu5f3bdb02018-06-25 04:48:48832
833 env->SetMethod(target, "registerDOMException", RegisterDOMException);
Anna Henningsene7a23672017-09-05 20:38:32834}
835
836} // anonymous namespace
837
838} // namespace worker
839} // namespace node
840
841NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging)