• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef SRC_NODE_MESSAGING_H_
2 #define SRC_NODE_MESSAGING_H_
3 
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5 
6 #include "env.h"
7 #include "node_mutex.h"
8 #include "sharedarraybuffer_metadata.h"
9 #include <list>
10 
11 namespace node {
12 namespace worker {
13 
14 class MessagePortData;
15 class MessagePort;
16 
17 typedef MaybeStackBuffer<v8::Local<v8::Value>, 8> TransferList;
18 
19 // Used to represent the in-flight structure of an object that is being
20 // transfered or cloned using postMessage().
21 class TransferData : public MemoryRetainer {
22  public:
23   // Deserialize this object on the receiving end after a .postMessage() call.
24   // - `context` may not be the same as `env->context()`. This method should
25   //    not produce JS objects coming from Contexts other than `context`.
26   // - `self` is a unique_ptr for the object that this is being called on.
27   // - The return value is treated like a `Maybe`, i.e. if `nullptr` is
28   //   returned, any further deserialization of the message is stopped and
29   //   control is returned to the event loop or JS as soon as possible.
30   virtual BaseObjectPtr<BaseObject> Deserialize(
31       Environment* env,
32       v8::Local<v8::Context> context,
33       std::unique_ptr<TransferData> self) = 0;
34   // FinalizeTransferWrite() is the counterpart to
35   // BaseObject::FinalizeTransferRead(). It is called right after the transfer
36   // data was created, and defaults to doing nothing. After this function,
37   // this object should not hold any more Isolate-specific data.
38   virtual v8::Maybe<bool> FinalizeTransferWrite(
39       v8::Local<v8::Context> context, v8::ValueSerializer* serializer);
40 };
41 
42 // Represents a single communication message.
43 class Message : public MemoryRetainer {
44  public:
45   // Create a Message with a specific underlying payload, in the format of the
46   // V8 ValueSerializer API. If `payload` is empty, this message indicates
47   // that the receiving message port should close itself.
48   explicit Message(MallocedBuffer<char>&& payload = MallocedBuffer<char>());
49 
50   Message(Message&& other) = default;
51   Message& operator=(Message&& other) = default;
52   Message& operator=(const Message&) = delete;
53   Message(const Message&) = delete;
54 
55   // Whether this is a message indicating that the port is to be closed.
56   // This is the last message to be received by a MessagePort.
57   bool IsCloseMessage() const;
58 
59   // Deserialize the contained JS value. May only be called once, and only
60   // after Serialize() has been called (e.g. by another thread).
61   v8::MaybeLocal<v8::Value> Deserialize(Environment* env,
62                                         v8::Local<v8::Context> context);
63 
64   // Serialize a JS value, and optionally transfer objects, into this message.
65   // The Message object retains ownership of all transferred objects until
66   // deserialization.
67   // The source_port parameter, if provided, will make Serialize() throw a
68   // "DataCloneError" DOMException if source_port is found in transfer_list.
69   v8::Maybe<bool> Serialize(Environment* env,
70                             v8::Local<v8::Context> context,
71                             v8::Local<v8::Value> input,
72                             const TransferList& transfer_list,
73                             v8::Local<v8::Object> source_port =
74                                 v8::Local<v8::Object>());
75 
76   // Internal method of Message that is called when a new SharedArrayBuffer
77   // object is encountered in the incoming value's structure.
78   void AddSharedArrayBuffer(const SharedArrayBufferMetadataReference& ref);
79   // Internal method of Message that is called once serialization finishes
80   // and that transfers ownership of `data` to this message.
81   void AddTransferable(std::unique_ptr<TransferData>&& data);
82   // Internal method of Message that is called when a new WebAssembly.Module
83   // object is encountered in the incoming value's structure.
84   uint32_t AddWASMModule(v8::WasmModuleObject::TransferrableModule&& mod);
85 
86   // The host objects that will be transferred, as recorded by Serialize()
87   // (e.g. MessagePorts).
88   // Used for warning user about posting the target MessagePort to itself,
89   // which will as a side effect destroy the communication channel.
transferables()90   const std::vector<std::unique_ptr<TransferData>>& transferables() const {
91     return transferables_;
92   }
93 
94   void MemoryInfo(MemoryTracker* tracker) const override;
95 
96   SET_MEMORY_INFO_NAME(Message)
97   SET_SELF_SIZE(Message)
98 
99  private:
100   MallocedBuffer<char> main_message_buf_;
101   std::vector<MallocedBuffer<char>> array_buffer_contents_;
102   std::vector<SharedArrayBufferMetadataReference> shared_array_buffers_;
103   std::vector<std::unique_ptr<TransferData>> transferables_;
104   std::vector<v8::WasmModuleObject::TransferrableModule> wasm_modules_;
105 
106   friend class MessagePort;
107 };
108 
109 // This contains all data for a `MessagePort` instance that is not tied to
110 // a specific Environment/Isolate/event loop, for easier transfer between those.
111 class MessagePortData : public TransferData {
112  public:
113   explicit MessagePortData(MessagePort* owner);
114   ~MessagePortData() override;
115 
116   MessagePortData(MessagePortData&& other) = delete;
117   MessagePortData& operator=(MessagePortData&& other) = delete;
118   MessagePortData(const MessagePortData& other) = delete;
119   MessagePortData& operator=(const MessagePortData& other) = delete;
120 
121   // Add a message to the incoming queue and notify the receiver.
122   // This may be called from any thread.
123   void AddToIncomingQueue(Message&& message);
124 
125   // Turns `a` and `b` into siblings, i.e. connects the sending side of one
126   // to the receiving side of the other. This is not thread-safe.
127   static void Entangle(MessagePortData* a, MessagePortData* b);
128 
129   // Removes any possible sibling. This is thread-safe (it acquires both
130   // `sibling_mutex_` and `mutex_`), and has to be because it is called once
131   // the corresponding JS handle handle wants to close
132   // which can happen on either side of a worker.
133   void Disentangle();
134 
135   void MemoryInfo(MemoryTracker* tracker) const override;
136   BaseObjectPtr<BaseObject> Deserialize(
137       Environment* env,
138       v8::Local<v8::Context> context,
139       std::unique_ptr<TransferData> self) override;
140 
141   SET_MEMORY_INFO_NAME(MessagePortData)
142   SET_SELF_SIZE(MessagePortData)
143 
144  private:
145   // This mutex protects all fields below it, with the exception of
146   // sibling_.
147   mutable Mutex mutex_;
148   std::list<Message> incoming_messages_;
149   MessagePort* owner_ = nullptr;
150   // This mutex protects the sibling_ field and is shared between two entangled
151   // MessagePorts. If both mutexes are acquired, this one needs to be
152   // acquired first.
153   std::shared_ptr<Mutex> sibling_mutex_ = std::make_shared<Mutex>();
154   MessagePortData* sibling_ = nullptr;
155 
156   friend class MessagePort;
157 };
158 
159 // A message port that receives messages from other threads, including
160 // the uv_async_t handle that is used to notify the current event loop of
161 // new incoming messages.
162 class MessagePort : public HandleWrap {
163  private:
164   // Create a new MessagePort. The `context` argument specifies the Context
165   // instance that is used for creating the values emitted from this port.
166   // This is called by MessagePort::New(), which is the public API used for
167   // creating MessagePort instances.
168   MessagePort(Environment* env,
169               v8::Local<v8::Context> context,
170               v8::Local<v8::Object> wrap);
171 
172  public:
173   ~MessagePort() override;
174 
175   // Create a new message port instance, optionally over an existing
176   // `MessagePortData` object.
177   static MessagePort* New(Environment* env,
178                           v8::Local<v8::Context> context,
179                           std::unique_ptr<MessagePortData> data = nullptr);
180 
181   // Send a message, i.e. deliver it into the sibling's incoming queue.
182   // If this port is closed, or if there is no sibling, this message is
183   // serialized with transfers, then silently discarded.
184   v8::Maybe<bool> PostMessage(Environment* env,
185                               v8::Local<v8::Value> message,
186                               const TransferList& transfer);
187 
188   // Start processing messages on this port as a receiving end.
189   void Start();
190   // Stop processing messages on this port as a receiving end.
191   void Stop();
192 
193   /* constructor */
194   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
195   /* prototype methods */
196   static void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
197   static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
198   static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
199   static void Drain(const v8::FunctionCallbackInfo<v8::Value>& args);
200   static void ReceiveMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
201 
202   /* static */
203   static void MoveToContext(const v8::FunctionCallbackInfo<v8::Value>& args);
204 
205   // Turns `a` and `b` into siblings, i.e. connects the sending side of one
206   // to the receiving side of the other. This is not thread-safe.
207   static void Entangle(MessagePort* a, MessagePort* b);
208   static void Entangle(MessagePort* a, MessagePortData* b);
209 
210   // Detach this port's data for transferring. After this, the MessagePortData
211   // is no longer associated with this handle, although it can still receive
212   // messages.
213   std::unique_ptr<MessagePortData> Detach();
214 
215   void Close(
216       v8::Local<v8::Value> close_callback = v8::Local<v8::Value>()) override;
217 
218   // Returns true if either data_ has been freed, or if the handle is being
219   // closed. Equivalent to the [[Detached]] internal slot in the HTML Standard.
220   //
221   // If checking if a JavaScript MessagePort object is detached, this method
222   // alone is often not enough, since the backing C++ MessagePort object may
223   // have been deleted already. For all intents and purposes, an object with a
224   // NULL pointer to the C++ MessagePort object is also detached.
225   inline bool IsDetached() const;
226 
227   TransferMode GetTransferMode() const override;
228   std::unique_ptr<TransferData> TransferForMessaging() override;
229 
230   void MemoryInfo(MemoryTracker* tracker) const override;
231   SET_MEMORY_INFO_NAME(MessagePort)
232   SET_SELF_SIZE(MessagePort)
233 
234  private:
235   void OnClose() override;
236   void OnMessage();
237   void TriggerAsync();
238   v8::MaybeLocal<v8::Value> ReceiveMessage(v8::Local<v8::Context> context,
239                                            bool only_if_receiving);
240 
241   std::unique_ptr<MessagePortData> data_ = nullptr;
242   bool receiving_messages_ = false;
243   uv_async_t async_;
244   v8::Global<v8::Function> emit_message_fn_;
245 
246   friend class MessagePortData;
247 };
248 
249 // Provide a base class from which JS classes that should be transferable or
250 // cloneable by postMesssage() can inherit.
251 // See e.g. FileHandle in internal/fs/promises.js for an example.
252 class JSTransferable : public BaseObject {
253  public:
254   JSTransferable(Environment* env, v8::Local<v8::Object> obj);
255   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
256 
257   TransferMode GetTransferMode() const override;
258   std::unique_ptr<TransferData> TransferForMessaging() override;
259   std::unique_ptr<TransferData> CloneForMessaging() const override;
260   v8::Maybe<std::vector<BaseObjectPtr<BaseObject>>>
261       NestedTransferables() const override;
262   v8::Maybe<bool> FinalizeTransferRead(
263       v8::Local<v8::Context> context,
264       v8::ValueDeserializer* deserializer) override;
265 
266   SET_NO_MEMORY_INFO()
267   SET_MEMORY_INFO_NAME(JSTransferable)
268   SET_SELF_SIZE(JSTransferable)
269 
270  private:
271   std::unique_ptr<TransferData> TransferOrClone(TransferMode mode) const;
272 
273   class Data : public TransferData {
274    public:
275     Data(std::string&& deserialize_info, v8::Global<v8::Value>&& data);
276 
277     BaseObjectPtr<BaseObject> Deserialize(
278         Environment* env,
279         v8::Local<v8::Context> context,
280         std::unique_ptr<TransferData> self) override;
281     v8::Maybe<bool> FinalizeTransferWrite(
282         v8::Local<v8::Context> context,
283         v8::ValueSerializer* serializer) override;
284 
285     SET_NO_MEMORY_INFO()
286     SET_MEMORY_INFO_NAME(JSTransferableTransferData)
287     SET_SELF_SIZE(Data)
288 
289    private:
290     std::string deserialize_info_;
291     v8::Global<v8::Value> data_;
292   };
293 };
294 
295 v8::Local<v8::FunctionTemplate> GetMessagePortConstructorTemplate(
296     Environment* env);
297 
298 }  // namespace worker
299 }  // namespace node
300 
301 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
302 
303 
304 #endif  // SRC_NODE_MESSAGING_H_
305