Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 1 | #include "debug_utils.h" |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 2 | #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 | |
| 11 | using v8::Array; |
| 12 | using v8::ArrayBuffer; |
| 13 | using v8::ArrayBufferCreationMode; |
| 14 | using v8::Context; |
| 15 | using v8::EscapableHandleScope; |
| 16 | using v8::Exception; |
| 17 | using v8::Function; |
| 18 | using v8::FunctionCallbackInfo; |
| 19 | using v8::FunctionTemplate; |
| 20 | using v8::HandleScope; |
| 21 | using v8::Isolate; |
| 22 | using v8::Just; |
| 23 | using v8::Local; |
| 24 | using v8::Maybe; |
| 25 | using v8::MaybeLocal; |
| 26 | using v8::Nothing; |
| 27 | using v8::Object; |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 28 | using v8::SharedArrayBuffer; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 29 | using v8::String; |
| 30 | using v8::Value; |
| 31 | using v8::ValueDeserializer; |
| 32 | using v8::ValueSerializer; |
| 33 | |
| 34 | namespace node { |
| 35 | namespace worker { |
| 36 | |
| 37 | Message::Message(MallocedBuffer<char>&& buffer) |
| 38 | : main_message_buf_(std::move(buffer)) {} |
| 39 | |
| 40 | namespace { |
| 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. |
| 44 | class DeserializerDelegate : public ValueDeserializer::Delegate { |
| 45 | public: |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 46 | DeserializerDelegate(Message* m, |
| 47 | Environment* env, |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 48 | const std::vector<MessagePort*>& message_ports, |
| 49 | const std::vector<Local<SharedArrayBuffer>>& |
| 50 | shared_array_buffers) |
Daniel Bevenius | 19dae6b | 2018-05-23 11:10:53 | [diff] [blame] | 51 | : message_ports_(message_ports), |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 52 | shared_array_buffers_(shared_array_buffers) {} |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 53 | |
| 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 Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 61 | return message_ports_[id]->object(isolate); |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 62 | }; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 63 | |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 64 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 70 | ValueDeserializer* deserializer = nullptr; |
| 71 | |
| 72 | private: |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 73 | const std::vector<MessagePort*>& message_ports_; |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 74 | const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 75 | }; |
| 76 | |
| 77 | } // anonymous namespace |
| 78 | |
| 79 | MaybeLocal<Value> Message::Deserialize(Environment* env, |
| 80 | Local<Context> context) { |
| 81 | EscapableHandleScope handle_scope(env->isolate()); |
| 82 | Context::Scope context_scope(context); |
| 83 | |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 84 | // 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 Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 100 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 112 | 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 Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 136 | void Message::AddSharedArrayBuffer( |
| 137 | SharedArrayBufferMetadataReference reference) { |
| 138 | shared_array_buffers_.push_back(reference); |
| 139 | } |
| 140 | |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 141 | void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) { |
| 142 | message_ports_.emplace_back(std::move(data)); |
| 143 | } |
| 144 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 145 | namespace { |
| 146 | |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 147 | void ThrowDataCloneException(Environment* env, Local<String> message) { |
Timothy Gu | 5f3bdb0 | 2018-06-25 04:48:48 | [diff] [blame] | 148 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 162 | // 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. |
| 165 | class 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 Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 171 | ThrowDataCloneException(env_, message); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 172 | } |
| 173 | |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 174 | 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 Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 183 | 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 204 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 213 | ValueSerializer* serializer = nullptr; |
| 214 | |
| 215 | private: |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 216 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 228 | Environment* env_; |
| 229 | Local<Context> context_; |
| 230 | Message* msg_; |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 231 | std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_; |
Anna Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 232 | std::vector<MessagePort*> ports_; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 233 | |
| 234 | friend class worker::Message; |
| 235 | }; |
| 236 | |
| 237 | } // anynomous namespace |
| 238 | |
| 239 | Maybe<bool> Message::Serialize(Environment* env, |
| 240 | Local<Context> context, |
| 241 | Local<Value> input, |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 242 | Local<Value> transfer_list_v, |
| 243 | Local<Object> source_port) { |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 244 | 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 262 | // Currently, we support ArrayBuffers and MessagePorts. |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 263 | 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 275 | } else if (env->message_port_constructor_template() |
| 276 | ->HasInstance(entry)) { |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 277 | // 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 285 | MessagePort* port = Unwrap<MessagePort>(entry.As<Object>()); |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 286 | 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 294 | delegate.ports_.push_back(port); |
| 295 | continue; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 296 | } |
| 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 Henningsen | 749a13b | 2017-10-07 21:39:02 | [diff] [blame] | 319 | delegate.Finish(); |
| 320 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 321 | // 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 Henningsen | 57e3015 | 2018-06-10 14:40:13 | [diff] [blame] | 328 | void 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 336 | MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { } |
| 337 | |
| 338 | MessagePortData::~MessagePortData() { |
| 339 | CHECK_EQ(owner_, nullptr); |
| 340 | Disentangle(); |
| 341 | } |
| 342 | |
Anna Henningsen | 57e3015 | 2018-06-10 14:40:13 | [diff] [blame] | 343 | void MessagePortData::MemoryInfo(MemoryTracker* tracker) const { |
| 344 | Mutex::ScopedLock lock(mutex_); |
| 345 | tracker->TrackThis(this); |
| 346 | tracker->TrackField("incoming_messages", incoming_messages_); |
| 347 | } |
| 348 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 349 | void 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 Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 354 | if (owner_ != nullptr) { |
| 355 | Debug(owner_, "Adding message to incoming queue"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 356 | owner_->TriggerAsync(); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 357 | } |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 358 | } |
| 359 | |
| 360 | bool MessagePortData::IsSiblingClosed() const { |
| 361 | Mutex::ScopedLock lock(*sibling_mutex_); |
| 362 | return sibling_ == nullptr; |
| 363 | } |
| 364 | |
| 365 | void 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 | |
| 373 | void MessagePortData::PingOwnerAfterDisentanglement() { |
| 374 | Mutex::ScopedLock lock(mutex_); |
| 375 | if (owner_ != nullptr) |
| 376 | owner_->TriggerAsync(); |
| 377 | } |
| 378 | |
| 379 | void 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 | |
| 400 | MessagePort::~MessagePort() { |
| 401 | if (data_) |
| 402 | data_->owner_ = nullptr; |
| 403 | } |
| 404 | |
| 405 | MessagePort::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 Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 431 | |
| 432 | Debug(this, "Created message port"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 433 | } |
| 434 | |
| 435 | void MessagePort::AddToIncomingQueue(Message&& message) { |
| 436 | data_->AddToIncomingQueue(std::move(message)); |
| 437 | } |
| 438 | |
| 439 | uv_async_t* MessagePort::async() { |
| 440 | return reinterpret_cast<uv_async_t*>(GetHandle()); |
| 441 | } |
| 442 | |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 443 | bool MessagePort::IsDetached() const { |
| 444 | return data_ == nullptr || IsHandleClosing(); |
| 445 | } |
| 446 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 447 | void MessagePort::TriggerAsync() { |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 448 | if (IsHandleClosing()) return; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 449 | CHECK_EQ(uv_async_send(async()), 0); |
| 450 | } |
| 451 | |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 452 | void MessagePort::Close(v8::Local<v8::Value> close_callback) { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 453 | Debug(this, "Closing message port, data set = %d", static_cast<int>(!!data_)); |
| 454 | |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 455 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 465 | void 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 | |
| 478 | MessagePort* 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 486 | |
| 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 Henningsen | d102a85 | 2018-09-23 17:26:30 | [diff] [blame^] | 492 | MessagePort* port = Unwrap<MessagePort>(instance); |
| 493 | CHECK_NOT_NULL(port); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 494 | 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 | |
| 505 | void MessagePort::OnMessage() { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 506 | Debug(this, "Running MessagePort::OnMessage()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 507 | HandleScope handle_scope(env()->isolate()); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 508 | Local<Context> context = object(env()->isolate())->CreationContext(); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 509 | |
| 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 Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 519 | |
| 520 | if (stop_event_loop_) { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 521 | Debug(this, "MessagePort stops loop as requested"); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 522 | CHECK(!data_->receiving_messages_); |
| 523 | uv_stop(env()->event_loop()); |
| 524 | break; |
| 525 | } |
| 526 | |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 527 | Debug(this, "MessagePort has message, receiving = %d", |
| 528 | static_cast<int>(data_->receiving_messages_)); |
| 529 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 530 | 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 Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 539 | Debug(this, "MessagePort drains queue because !can_call_into_js()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 540 | // 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 553 | 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 | |
| 567 | bool MessagePort::IsSiblingClosed() const { |
| 568 | CHECK(data_); |
| 569 | return data_->IsSiblingClosed(); |
| 570 | } |
| 571 | |
| 572 | void MessagePort::OnClose() { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 573 | Debug(this, "MessagePort::OnClose()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 574 | if (data_) { |
| 575 | data_->owner_ = nullptr; |
| 576 | data_->Disentangle(); |
| 577 | } |
| 578 | data_.reset(); |
| 579 | delete async(); |
| 580 | } |
| 581 | |
| 582 | std::unique_ptr<MessagePortData> MessagePort::Detach() { |
| 583 | Mutex::ScopedLock lock(data_->mutex_); |
| 584 | data_->owner_ = nullptr; |
| 585 | return std::move(data_); |
| 586 | } |
| 587 | |
| 588 | |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 589 | Maybe<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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 595 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 596 | Message msg; |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 597 | |
| 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 605 | } |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 606 | 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 630 | } |
| 631 | |
| 632 | void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) { |
| 633 | Environment* env = Environment::GetCurrent(args); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 634 | if (args.Length() == 0) { |
| 635 | return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to " |
| 636 | "MessagePort.postMessage"); |
| 637 | } |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 638 | |
| 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 652 | } |
| 653 | |
| 654 | void MessagePort::Start() { |
| 655 | Mutex::ScopedLock lock(data_->mutex_); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 656 | Debug(this, "Start receiving messages"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 657 | data_->receiving_messages_ = true; |
| 658 | if (!data_->incoming_messages_.empty()) |
| 659 | TriggerAsync(); |
| 660 | } |
| 661 | |
| 662 | void MessagePort::Stop() { |
| 663 | Mutex::ScopedLock lock(data_->mutex_); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 664 | Debug(this, "Stop receiving messages"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 665 | data_->receiving_messages_ = false; |
| 666 | } |
| 667 | |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 668 | void MessagePort::StopEventLoop() { |
| 669 | Mutex::ScopedLock lock(data_->mutex_); |
| 670 | data_->receiving_messages_ = false; |
| 671 | stop_event_loop_ = true; |
| 672 | |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 673 | Debug(this, "Received StopEventLoop request"); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 674 | TriggerAsync(); |
| 675 | } |
| 676 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 677 | void 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 | |
| 688 | void 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 Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 699 | void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) { |
| 700 | MessagePort* port; |
| 701 | ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); |
| 702 | port->OnMessage(); |
| 703 | } |
| 704 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 705 | void MessagePort::Entangle(MessagePort* a, MessagePort* b) { |
| 706 | Entangle(a, b->data_.get()); |
| 707 | } |
| 708 | |
| 709 | void MessagePort::Entangle(MessagePort* a, MessagePortData* b) { |
| 710 | MessagePortData::Entangle(a->data_.get(), b); |
| 711 | } |
| 712 | |
| 713 | MaybeLocal<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 Moss | 45732c7 | 2018-07-11 17:53:05 | [diff] [blame] | 727 | HandleWrap::AddWrapMethods(env, m); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 728 | |
| 729 | env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage); |
| 730 | env->SetProtoMethod(m, "start", MessagePort::Start); |
| 731 | env->SetProtoMethod(m, "stop", MessagePort::Stop); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 732 | env->SetProtoMethod(m, "drain", MessagePort::Drain); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 733 | |
| 734 | env->set_message_port_constructor_template(m); |
| 735 | } |
| 736 | |
| 737 | return GetMessagePortConstructor(env, context); |
| 738 | } |
| 739 | |
| 740 | namespace { |
| 741 | |
| 742 | static 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 Gu | 5f3bdb0 | 2018-06-25 04:48:48 | [diff] [blame] | 762 | static 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 Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 769 | static 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 Gu | 5f3bdb0 | 2018-06-25 04:48:48 | [diff] [blame] | 789 | |
| 790 | env->SetMethod(target, "registerDOMException", RegisterDOMException); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 791 | } |
| 792 | |
| 793 | } // anonymous namespace |
| 794 | |
| 795 | } // namespace worker |
| 796 | } // namespace node |
| 797 | |
| 798 | NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging) |