worker: use special message as MessagePort close command · nodejs/node@e004d42 · GitHub
Skip to content

Commit e004d42

Browse files
addaleaxtargos
authored andcommitted
worker: use special message as MessagePort close command
When a `MessagePort` connected to another `MessagePort` closes, the latter `MessagePort` will be closed as well. Until now, this is done by testing whether the ports are still entangled after processing messages. This leaves open a race condition window in which messages sent just before the closure can be lost when timing is unfortunate. (A description of the timing is in the test file.) This can be addressed by using a special message instead, which is the last message received by a `MessagePort`. This way, all previously sent messages are processed first. Fixes: #22762 PR-URL: #27705 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent b7ed4d7 commit e004d42

3 files changed

Lines changed: 71 additions & 36 deletions

File tree

src/node_messaging.cc

Lines changed: 26 additions & 27 deletions

src/node_messaging.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,20 @@ class MessagePort;
1717
// Represents a single communication message.
1818
class Message : public MemoryRetainer {
1919
public:
20+
// Create a Message with a specific underlying payload, in the format of the
21+
// V8 ValueSerializer API. If `payload` is empty, this message indicates
22+
// that the receiving message port should close itself.
2023
explicit Message(MallocedBuffer<char>&& payload = MallocedBuffer<char>());
2124

2225
Message(Message&& other) = default;
2326
Message& operator=(Message&& other) = default;
2427
Message& operator=(const Message&) = delete;
2528
Message(const Message&) = delete;
2629

30+
// Whether this is a message indicating that the port is to be closed.
31+
// This is the last message to be received by a MessagePort.
32+
bool IsCloseMessage() const;
33+
2734
// Deserialize the contained JS value. May only be called once, and only
2835
// after Serialize() has been called (e.g. by another thread).
2936
v8::MaybeLocal<v8::Value> Deserialize(Environment* env,
@@ -89,10 +96,6 @@ class MessagePortData : public MemoryRetainer {
8996
// This may be called from any thread.
9097
void AddToIncomingQueue(Message&& message);
9198

92-
// Returns true if and only this MessagePort is currently not entangled
93-
// with another message port.
94-
bool IsSiblingClosed() const;
95-
9699
// Turns `a` and `b` into siblings, i.e. connects the sending side of one
97100
// to the receiving side of the other. This is not thread-safe.
98101
static void Entangle(MessagePortData* a, MessagePortData* b);
@@ -109,10 +112,6 @@ class MessagePortData : public MemoryRetainer {
109112
SET_SELF_SIZE(MessagePortData)
110113

111114
private:
112-
// After disentangling this message port, the owner handle (if any)
113-
// is asynchronously triggered, so that it can close down naturally.
114-
void PingOwnerAfterDisentanglement();
115-
116115
// This mutex protects all fields below it, with the exception of
117116
// sibling_.
118117
mutable Mutex mutex_;
@@ -178,7 +177,6 @@ class MessagePort : public HandleWrap {
178177
// messages.
179178
std::unique_ptr<MessagePortData> Detach();
180179

181-
bool IsSiblingClosed() const;
182180
void Close(
183181
v8::Local<v8::Value> close_callback = v8::Local<v8::Value>()) override;
184182

Lines changed: 38 additions & 0 deletions

0 commit comments

Comments
 (0)