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; |
Brandon Smith | 13340d4 | 2018-10-07 01:09:29 | [diff] [blame^] | 101 | // Attach all transferred SharedArrayBuffers to their new Isolate. |
Anna Henningsen | b040404 | 2018-05-13 17:39:32 | [diff] [blame] | 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 | |
Brandon Smith | 13340d4 | 2018-10-07 01:09:29 | [diff] [blame^] | 119 | // Attach all transferred ArrayBuffers to their new Isolate. |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 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 | |
Brandon Smith | 13340d4 | 2018-10-07 01:09:29 | [diff] [blame^] | 237 | } // anonymous namespace |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 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 { |
Anna Henningsen | 57e3015 | 2018-06-10 14:40:13 | [diff] [blame] | 329 | tracker->TrackField("array_buffer_contents", array_buffer_contents_); |
| 330 | tracker->TrackFieldWithSize("shared_array_buffers", |
| 331 | shared_array_buffers_.size() * sizeof(shared_array_buffers_[0])); |
| 332 | tracker->TrackField("message_ports", message_ports_); |
| 333 | } |
| 334 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 335 | MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { } |
| 336 | |
| 337 | MessagePortData::~MessagePortData() { |
| 338 | CHECK_EQ(owner_, nullptr); |
| 339 | Disentangle(); |
| 340 | } |
| 341 | |
Anna Henningsen | 57e3015 | 2018-06-10 14:40:13 | [diff] [blame] | 342 | void MessagePortData::MemoryInfo(MemoryTracker* tracker) const { |
| 343 | Mutex::ScopedLock lock(mutex_); |
Anna Henningsen | 57e3015 | 2018-06-10 14:40:13 | [diff] [blame] | 344 | tracker->TrackField("incoming_messages", incoming_messages_); |
| 345 | } |
| 346 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 347 | void MessagePortData::AddToIncomingQueue(Message&& message) { |
| 348 | // This function will be called by other threads. |
| 349 | Mutex::ScopedLock lock(mutex_); |
| 350 | incoming_messages_.emplace_back(std::move(message)); |
| 351 | |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 352 | if (owner_ != nullptr) { |
| 353 | Debug(owner_, "Adding message to incoming queue"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 354 | owner_->TriggerAsync(); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 355 | } |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | bool MessagePortData::IsSiblingClosed() const { |
| 359 | Mutex::ScopedLock lock(*sibling_mutex_); |
| 360 | return sibling_ == nullptr; |
| 361 | } |
| 362 | |
| 363 | void MessagePortData::Entangle(MessagePortData* a, MessagePortData* b) { |
| 364 | CHECK_EQ(a->sibling_, nullptr); |
| 365 | CHECK_EQ(b->sibling_, nullptr); |
| 366 | a->sibling_ = b; |
| 367 | b->sibling_ = a; |
| 368 | a->sibling_mutex_ = b->sibling_mutex_; |
| 369 | } |
| 370 | |
| 371 | void MessagePortData::PingOwnerAfterDisentanglement() { |
| 372 | Mutex::ScopedLock lock(mutex_); |
| 373 | if (owner_ != nullptr) |
| 374 | owner_->TriggerAsync(); |
| 375 | } |
| 376 | |
| 377 | void MessagePortData::Disentangle() { |
| 378 | // Grab a copy of the sibling mutex, then replace it so that each sibling |
| 379 | // has its own sibling_mutex_ now. |
| 380 | std::shared_ptr<Mutex> sibling_mutex = sibling_mutex_; |
| 381 | Mutex::ScopedLock sibling_lock(*sibling_mutex); |
| 382 | sibling_mutex_ = std::make_shared<Mutex>(); |
| 383 | |
| 384 | MessagePortData* sibling = sibling_; |
| 385 | if (sibling_ != nullptr) { |
| 386 | sibling_->sibling_ = nullptr; |
| 387 | sibling_ = nullptr; |
| 388 | } |
| 389 | |
| 390 | // We close MessagePorts after disentanglement, so we trigger the |
| 391 | // corresponding uv_async_t to let them know that this happened. |
| 392 | PingOwnerAfterDisentanglement(); |
| 393 | if (sibling != nullptr) { |
| 394 | sibling->PingOwnerAfterDisentanglement(); |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | MessagePort::~MessagePort() { |
| 399 | if (data_) |
| 400 | data_->owner_ = nullptr; |
| 401 | } |
| 402 | |
| 403 | MessagePort::MessagePort(Environment* env, |
| 404 | Local<Context> context, |
| 405 | Local<Object> wrap) |
| 406 | : HandleWrap(env, |
| 407 | wrap, |
| 408 | reinterpret_cast<uv_handle_t*>(new uv_async_t()), |
| 409 | AsyncWrap::PROVIDER_MESSAGEPORT), |
| 410 | data_(new MessagePortData(this)) { |
| 411 | auto onmessage = [](uv_async_t* handle) { |
| 412 | // Called when data has been put into the queue. |
| 413 | MessagePort* channel = static_cast<MessagePort*>(handle->data); |
| 414 | channel->OnMessage(); |
| 415 | }; |
| 416 | CHECK_EQ(uv_async_init(env->event_loop(), |
| 417 | async(), |
| 418 | onmessage), 0); |
| 419 | async()->data = static_cast<void*>(this); |
| 420 | |
| 421 | Local<Value> fn; |
Anna Henningsen | 2d65e67 | 2018-09-23 17:10:54 | [diff] [blame] | 422 | if (!wrap->Get(context, env->oninit_symbol()).ToLocal(&fn)) |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 423 | return; |
| 424 | |
| 425 | if (fn->IsFunction()) { |
| 426 | Local<Function> init = fn.As<Function>(); |
| 427 | USE(init->Call(context, wrap, 0, nullptr)); |
| 428 | } |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 429 | |
| 430 | Debug(this, "Created message port"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 431 | } |
| 432 | |
| 433 | void MessagePort::AddToIncomingQueue(Message&& message) { |
| 434 | data_->AddToIncomingQueue(std::move(message)); |
| 435 | } |
| 436 | |
| 437 | uv_async_t* MessagePort::async() { |
| 438 | return reinterpret_cast<uv_async_t*>(GetHandle()); |
| 439 | } |
| 440 | |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 441 | bool MessagePort::IsDetached() const { |
| 442 | return data_ == nullptr || IsHandleClosing(); |
| 443 | } |
| 444 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 445 | void MessagePort::TriggerAsync() { |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 446 | if (IsHandleClosing()) return; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 447 | CHECK_EQ(uv_async_send(async()), 0); |
| 448 | } |
| 449 | |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 450 | void MessagePort::Close(v8::Local<v8::Value> close_callback) { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 451 | Debug(this, "Closing message port, data set = %d", static_cast<int>(!!data_)); |
| 452 | |
Anna Henningsen | 22c826f | 2018-06-07 12:43:45 | [diff] [blame] | 453 | if (data_) { |
| 454 | // Wrap this call with accessing the mutex, so that TriggerAsync() |
| 455 | // can check IsHandleClosing() without race conditions. |
| 456 | Mutex::ScopedLock sibling_lock(data_->mutex_); |
| 457 | HandleWrap::Close(close_callback); |
| 458 | } else { |
| 459 | HandleWrap::Close(close_callback); |
| 460 | } |
| 461 | } |
| 462 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 463 | void MessagePort::New(const FunctionCallbackInfo<Value>& args) { |
| 464 | Environment* env = Environment::GetCurrent(args); |
| 465 | if (!args.IsConstructCall()) { |
| 466 | THROW_ERR_CONSTRUCT_CALL_REQUIRED(env); |
| 467 | return; |
| 468 | } |
| 469 | |
| 470 | Local<Context> context = args.This()->CreationContext(); |
| 471 | Context::Scope context_scope(context); |
| 472 | |
| 473 | new MessagePort(env, context, args.This()); |
| 474 | } |
| 475 | |
| 476 | MessagePort* MessagePort::New( |
| 477 | Environment* env, |
| 478 | Local<Context> context, |
| 479 | std::unique_ptr<MessagePortData> data) { |
| 480 | Context::Scope context_scope(context); |
| 481 | Local<Function> ctor; |
| 482 | if (!GetMessagePortConstructor(env, context).ToLocal(&ctor)) |
| 483 | return nullptr; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 484 | |
| 485 | // Construct a new instance, then assign the listener instance and possibly |
| 486 | // the MessagePortData to it. |
| 487 | Local<Object> instance; |
| 488 | if (!ctor->NewInstance(context).ToLocal(&instance)) |
| 489 | return nullptr; |
Anna Henningsen | d102a85 | 2018-09-23 17:26:30 | [diff] [blame] | 490 | MessagePort* port = Unwrap<MessagePort>(instance); |
| 491 | CHECK_NOT_NULL(port); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 492 | if (data) { |
| 493 | port->Detach(); |
| 494 | port->data_ = std::move(data); |
| 495 | port->data_->owner_ = port; |
| 496 | // If the existing MessagePortData object had pending messages, this is |
| 497 | // the easiest way to run that queue. |
| 498 | port->TriggerAsync(); |
| 499 | } |
| 500 | return port; |
| 501 | } |
| 502 | |
| 503 | void MessagePort::OnMessage() { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 504 | Debug(this, "Running MessagePort::OnMessage()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 505 | HandleScope handle_scope(env()->isolate()); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 506 | Local<Context> context = object(env()->isolate())->CreationContext(); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 507 | |
| 508 | // data_ can only ever be modified by the owner thread, so no need to lock. |
| 509 | // However, the message port may be transferred while it is processing |
| 510 | // messages, so we need to check that this handle still owns its `data_` field |
| 511 | // on every iteration. |
| 512 | while (data_) { |
| 513 | Message received; |
| 514 | { |
| 515 | // Get the head of the message queue. |
| 516 | Mutex::ScopedLock lock(data_->mutex_); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 517 | |
| 518 | if (stop_event_loop_) { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 519 | Debug(this, "MessagePort stops loop as requested"); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 520 | CHECK(!data_->receiving_messages_); |
| 521 | uv_stop(env()->event_loop()); |
| 522 | break; |
| 523 | } |
| 524 | |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 525 | Debug(this, "MessagePort has message, receiving = %d", |
| 526 | static_cast<int>(data_->receiving_messages_)); |
| 527 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 528 | if (!data_->receiving_messages_) |
| 529 | break; |
| 530 | if (data_->incoming_messages_.empty()) |
| 531 | break; |
| 532 | received = std::move(data_->incoming_messages_.front()); |
| 533 | data_->incoming_messages_.pop_front(); |
| 534 | } |
| 535 | |
| 536 | if (!env()->can_call_into_js()) { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 537 | Debug(this, "MessagePort drains queue because !can_call_into_js()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 538 | // In this case there is nothing to do but to drain the current queue. |
| 539 | continue; |
| 540 | } |
| 541 | |
| 542 | { |
| 543 | // Call the JS .onmessage() callback. |
| 544 | HandleScope handle_scope(env()->isolate()); |
| 545 | Context::Scope context_scope(context); |
| 546 | Local<Value> args[] = { |
| 547 | received.Deserialize(env(), context).FromMaybe(Local<Value>()) |
| 548 | }; |
| 549 | |
| 550 | if (args[0].IsEmpty() || |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 551 | MakeCallback(env()->onmessage_string(), 1, args).IsEmpty()) { |
| 552 | // Re-schedule OnMessage() execution in case of failure. |
| 553 | if (data_) |
| 554 | TriggerAsync(); |
| 555 | return; |
| 556 | } |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | if (data_ && data_->IsSiblingClosed()) { |
| 561 | Close(); |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | bool MessagePort::IsSiblingClosed() const { |
| 566 | CHECK(data_); |
| 567 | return data_->IsSiblingClosed(); |
| 568 | } |
| 569 | |
| 570 | void MessagePort::OnClose() { |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 571 | Debug(this, "MessagePort::OnClose()"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 572 | if (data_) { |
| 573 | data_->owner_ = nullptr; |
| 574 | data_->Disentangle(); |
| 575 | } |
| 576 | data_.reset(); |
| 577 | delete async(); |
| 578 | } |
| 579 | |
| 580 | std::unique_ptr<MessagePortData> MessagePort::Detach() { |
| 581 | Mutex::ScopedLock lock(data_->mutex_); |
| 582 | data_->owner_ = nullptr; |
| 583 | return std::move(data_); |
| 584 | } |
| 585 | |
| 586 | |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 587 | Maybe<bool> MessagePort::PostMessage(Environment* env, |
| 588 | Local<Value> message_v, |
| 589 | Local<Value> transfer_v) { |
| 590 | Isolate* isolate = env->isolate(); |
| 591 | Local<Object> obj = object(isolate); |
| 592 | Local<Context> context = obj->CreationContext(); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 593 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 594 | Message msg; |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 595 | |
| 596 | // Per spec, we need to both check if transfer list has the source port, and |
| 597 | // serialize the input message, even if the MessagePort is closed or detached. |
| 598 | |
| 599 | Maybe<bool> serialization_maybe = |
| 600 | msg.Serialize(env, context, message_v, transfer_v, obj); |
| 601 | if (data_ == nullptr) { |
| 602 | return serialization_maybe; |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 603 | } |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 604 | if (serialization_maybe.IsNothing()) { |
| 605 | return Nothing<bool>(); |
| 606 | } |
| 607 | |
| 608 | Mutex::ScopedLock lock(*data_->sibling_mutex_); |
| 609 | bool doomed = false; |
| 610 | |
| 611 | // Check if the target port is posted to itself. |
| 612 | if (data_->sibling_ != nullptr) { |
| 613 | for (const auto& port_data : msg.message_ports()) { |
| 614 | if (data_->sibling_ == port_data.get()) { |
| 615 | doomed = true; |
| 616 | ProcessEmitWarning(env, "The target port was posted to itself, and " |
| 617 | "the communication channel was lost"); |
| 618 | break; |
| 619 | } |
| 620 | } |
| 621 | } |
| 622 | |
| 623 | if (data_->sibling_ == nullptr || doomed) |
| 624 | return Just(true); |
| 625 | |
| 626 | data_->sibling_->AddToIncomingQueue(std::move(msg)); |
| 627 | return Just(true); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 628 | } |
| 629 | |
| 630 | void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) { |
| 631 | Environment* env = Environment::GetCurrent(args); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 632 | if (args.Length() == 0) { |
| 633 | return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to " |
| 634 | "MessagePort.postMessage"); |
| 635 | } |
Timothy Gu | f374d6a | 2018-06-25 03:10:37 | [diff] [blame] | 636 | |
| 637 | MessagePort* port = Unwrap<MessagePort>(args.This()); |
| 638 | // Even if the backing MessagePort object has already been deleted, we still |
| 639 | // want to serialize the message to ensure spec-compliant behavior w.r.t. |
| 640 | // transfers. |
| 641 | if (port == nullptr) { |
| 642 | Message msg; |
| 643 | Local<Object> obj = args.This(); |
| 644 | Local<Context> context = obj->CreationContext(); |
| 645 | USE(msg.Serialize(env, context, args[0], args[1], obj)); |
| 646 | return; |
| 647 | } |
| 648 | |
| 649 | port->PostMessage(env, args[0], args[1]); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 650 | } |
| 651 | |
| 652 | void MessagePort::Start() { |
| 653 | Mutex::ScopedLock lock(data_->mutex_); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 654 | Debug(this, "Start receiving messages"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 655 | data_->receiving_messages_ = true; |
| 656 | if (!data_->incoming_messages_.empty()) |
| 657 | TriggerAsync(); |
| 658 | } |
| 659 | |
| 660 | void MessagePort::Stop() { |
| 661 | Mutex::ScopedLock lock(data_->mutex_); |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 662 | Debug(this, "Stop receiving messages"); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 663 | data_->receiving_messages_ = false; |
| 664 | } |
| 665 | |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 666 | void MessagePort::StopEventLoop() { |
| 667 | Mutex::ScopedLock lock(data_->mutex_); |
| 668 | data_->receiving_messages_ = false; |
| 669 | stop_event_loop_ = true; |
| 670 | |
Anna Henningsen | 018d618 | 2018-06-20 15:10:06 | [diff] [blame] | 671 | Debug(this, "Received StopEventLoop request"); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 672 | TriggerAsync(); |
| 673 | } |
| 674 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 675 | void MessagePort::Start(const FunctionCallbackInfo<Value>& args) { |
| 676 | Environment* env = Environment::GetCurrent(args); |
| 677 | MessagePort* port; |
| 678 | ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); |
| 679 | if (!port->data_) { |
| 680 | THROW_ERR_CLOSED_MESSAGE_PORT(env); |
| 681 | return; |
| 682 | } |
| 683 | port->Start(); |
| 684 | } |
| 685 | |
| 686 | void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) { |
| 687 | Environment* env = Environment::GetCurrent(args); |
| 688 | MessagePort* port; |
| 689 | ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); |
| 690 | if (!port->data_) { |
| 691 | THROW_ERR_CLOSED_MESSAGE_PORT(env); |
| 692 | return; |
| 693 | } |
| 694 | port->Stop(); |
| 695 | } |
| 696 | |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 697 | void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) { |
| 698 | MessagePort* port; |
| 699 | ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); |
| 700 | port->OnMessage(); |
| 701 | } |
| 702 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 703 | void MessagePort::Entangle(MessagePort* a, MessagePort* b) { |
| 704 | Entangle(a, b->data_.get()); |
| 705 | } |
| 706 | |
| 707 | void MessagePort::Entangle(MessagePort* a, MessagePortData* b) { |
| 708 | MessagePortData::Entangle(a->data_.get(), b); |
| 709 | } |
| 710 | |
| 711 | MaybeLocal<Function> GetMessagePortConstructor( |
| 712 | Environment* env, Local<Context> context) { |
| 713 | // Factor generating the MessagePort JS constructor into its own piece |
| 714 | // of code, because it is needed early on in the child environment setup. |
| 715 | Local<FunctionTemplate> templ = env->message_port_constructor_template(); |
| 716 | if (!templ.IsEmpty()) |
| 717 | return templ->GetFunction(context); |
| 718 | |
| 719 | { |
| 720 | Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New); |
| 721 | m->SetClassName(env->message_port_constructor_string()); |
| 722 | m->InstanceTemplate()->SetInternalFieldCount(1); |
Anna Henningsen | d527dde | 2018-09-23 17:24:33 | [diff] [blame] | 723 | m->Inherit(HandleWrap::GetConstructorTemplate(env)); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 724 | |
| 725 | env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage); |
| 726 | env->SetProtoMethod(m, "start", MessagePort::Start); |
| 727 | env->SetProtoMethod(m, "stop", MessagePort::Stop); |
Anna Henningsen | 0df031a | 2017-09-01 15:03:41 | [diff] [blame] | 728 | env->SetProtoMethod(m, "drain", MessagePort::Drain); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 729 | |
| 730 | env->set_message_port_constructor_template(m); |
| 731 | } |
| 732 | |
| 733 | return GetMessagePortConstructor(env, context); |
| 734 | } |
| 735 | |
| 736 | namespace { |
| 737 | |
| 738 | static void MessageChannel(const FunctionCallbackInfo<Value>& args) { |
| 739 | Environment* env = Environment::GetCurrent(args); |
| 740 | if (!args.IsConstructCall()) { |
| 741 | THROW_ERR_CONSTRUCT_CALL_REQUIRED(env); |
| 742 | return; |
| 743 | } |
| 744 | |
| 745 | Local<Context> context = args.This()->CreationContext(); |
| 746 | Context::Scope context_scope(context); |
| 747 | |
| 748 | MessagePort* port1 = MessagePort::New(env, context); |
| 749 | MessagePort* port2 = MessagePort::New(env, context); |
| 750 | MessagePort::Entangle(port1, port2); |
| 751 | |
| 752 | args.This()->Set(env->context(), env->port1_string(), port1->object()) |
| 753 | .FromJust(); |
| 754 | args.This()->Set(env->context(), env->port2_string(), port2->object()) |
| 755 | .FromJust(); |
| 756 | } |
| 757 | |
Timothy Gu | 5f3bdb0 | 2018-06-25 04:48:48 | [diff] [blame] | 758 | static void RegisterDOMException(const FunctionCallbackInfo<Value>& args) { |
| 759 | Environment* env = Environment::GetCurrent(args); |
| 760 | CHECK_EQ(args.Length(), 1); |
| 761 | CHECK(args[0]->IsFunction()); |
| 762 | env->set_domexception_function(args[0].As<Function>()); |
| 763 | } |
| 764 | |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 765 | static void InitMessaging(Local<Object> target, |
| 766 | Local<Value> unused, |
| 767 | Local<Context> context, |
| 768 | void* priv) { |
| 769 | Environment* env = Environment::GetCurrent(context); |
| 770 | |
| 771 | { |
| 772 | Local<String> message_channel_string = |
| 773 | FIXED_ONE_BYTE_STRING(env->isolate(), "MessageChannel"); |
| 774 | Local<FunctionTemplate> templ = env->NewFunctionTemplate(MessageChannel); |
| 775 | templ->SetClassName(message_channel_string); |
| 776 | target->Set(env->context(), |
| 777 | message_channel_string, |
| 778 | templ->GetFunction(context).ToLocalChecked()).FromJust(); |
| 779 | } |
| 780 | |
| 781 | target->Set(context, |
| 782 | env->message_port_constructor_string(), |
| 783 | GetMessagePortConstructor(env, context).ToLocalChecked()) |
| 784 | .FromJust(); |
Timothy Gu | 5f3bdb0 | 2018-06-25 04:48:48 | [diff] [blame] | 785 | |
| 786 | env->SetMethod(target, "registerDOMException", RegisterDOMException); |
Anna Henningsen | e7a2367 | 2017-09-05 20:38:32 | [diff] [blame] | 787 | } |
| 788 | |
| 789 | } // anonymous namespace |
| 790 | |
| 791 | } // namespace worker |
| 792 | } // namespace node |
| 793 | |
| 794 | NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging) |