| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f447acd commit d1f372f
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -85,14 +85,16 @@ to stringify. | |||
| 85 | 85 | ||
| 86 | 86 | `transferList` may be a list of `ArrayBuffer` and `MessagePort` objects. | |
| 87 | 87 | After transferring, they will not be usable on the sending side of the channel | |
| 88 | - anymore (even if they are not contained in `value`). | ||
| 88 | + anymore (even if they are not contained in `value`). Unlike with | ||
| 89 | + [child processes][], transferring handles such as network sockets is currently | ||
| 90 | + not supported. | ||
| 91 | + | ||
| 92 | + If `value` contains [`SharedArrayBuffer`][] instances, those will be accessible | ||
| 93 | + from either thread. They cannot be listed in `transferList`. | ||
| 89 | 94 | ||
| 90 | 95 | `value` may still contain `ArrayBuffer` instances that are not in | |
| 91 | 96 | `transferList`; in that case, the underlying memory is copied rather than moved. | |
| 92 | 97 | ||
| 93 | - For more information on the serialization and deserialization mechanisms | ||
| 94 | - behind this API, see the [serialization API of the `v8` module][v8.serdes]. | ||
| 95 | - | ||
| 96 | 98 | Because the object cloning uses the structured clone algorithm, | |
| 97 | 99 | non-enumerable properties, property accessors, and object prototypes are | |
| 98 | 100 | not preserved. In particular, [`Buffer`][] objects will be read as | |
@@ -101,6 +103,9 @@ plain [`Uint8Array`][]s on the receiving side. | |||
| 101 | 103 | The message object will be cloned immediately, and can be modified after | |
| 102 | 104 | posting without having side effects. | |
| 103 | 105 | ||
| 106 | + For more information on the serialization and deserialization mechanisms | ||
| 107 | + behind this API, see the [serialization API of the `v8` module][v8.serdes]. | ||
| 108 | + | ||
| 104 | 109 | ### port.ref() | |
| 105 | 110 | <!-- YAML | |
| 106 | 111 | added: REPLACEME | |
@@ -137,10 +142,12 @@ be `ref()`ed and `unref()`ed automatically depending on whether | |||
| 137 | 142 | listeners for the event exist. | |
| 138 | 143 | ||
| 139 | 144 | [`Buffer`]: buffer.html | |
| 145 | + [child processes]: child_process.html | ||
| 140 | 146 | [`EventEmitter`]: events.html | |
| 141 | 147 | [`MessagePort`]: #worker_class_messageport | |
| 142 | 148 | [`port.postMessage()`]: #worker_port_postmessage_value_transferlist | |
| 143 | 149 | [v8.serdes]: v8.html#v8_serialization_api | |
| 150 | + [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer | ||
| 144 | 151 | [`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array | |
| 145 | 152 | [browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort | |
| 146 | 153 | [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -352,6 +352,7 @@ | |||
| 352 | 352 | 'src/node_i18n.cc', | |
| 353 | 353 | 'src/pipe_wrap.cc', | |
| 354 | 354 | 'src/process_wrap.cc', | |
| 355 | + 'src/sharedarraybuffer_metadata.cc', | ||
| 355 | 356 | 'src/signal_wrap.cc', | |
| 356 | 357 | 'src/spawn_sync.cc', | |
| 357 | 358 | 'src/string_bytes.cc', | |
@@ -411,6 +412,7 @@ | |||
| 411 | 412 | 'src/udp_wrap.h', | |
| 412 | 413 | 'src/req_wrap.h', | |
| 413 | 414 | 'src/req_wrap-inl.h', | |
| 415 | + 'src/sharedarraybuffer_metadata.h', | ||
| 414 | 416 | 'src/string_bytes.h', | |
| 415 | 417 | 'src/string_decoder.h', | |
| 416 | 418 | 'src/string_decoder-inl.h', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -106,6 +106,7 @@ struct PackageConfig { | |||
| 106 | 106 | V(decorated_private_symbol, "node:decorated") \ | |
| 107 | 107 | V(napi_env, "node:napi:env") \ | |
| 108 | 108 | V(napi_wrapper, "node:napi:wrapper") \ | |
| 109 | + V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \ | ||
| 109 | 110 | ||
| 110 | 111 | // Symbols are per-isolate primitives but Environment proxies them | |
| 111 | 112 | // for the sake of convenience. | |
@@ -338,6 +339,7 @@ struct PackageConfig { | |||
| 338 | 339 | V(promise_wrap_template, v8::ObjectTemplate) \ | |
| 339 | 340 | V(push_values_to_array_function, v8::Function) \ | |
| 340 | 341 | V(randombytes_constructor_template, v8::ObjectTemplate) \ | |
| 342 | + V(sab_lifetimepartner_constructor_template, v8::FunctionTemplate) \ | ||
| 341 | 343 | V(script_context_constructor_template, v8::FunctionTemplate) \ | |
| 342 | 344 | V(script_data_constructor_function, v8::Function) \ | |
| 343 | 345 | V(secure_context_constructor_template, v8::FunctionTemplate) \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,7 @@ namespace node { | |||
| 31 | 31 | V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \ | |
| 32 | 32 | V(ERR_MISSING_MODULE, Error) \ | |
| 33 | 33 | V(ERR_STRING_TOO_LONG, Error) \ | |
| 34 | + V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ | ||
| 34 | 35 | ||
| 35 | 36 | #define V(code, type) \ | |
| 36 | 37 | inline v8::Local<v8::Value> code(v8::Isolate* isolate, \ | |
@@ -60,7 +61,9 @@ namespace node { | |||
| 60 | 61 | V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \ | |
| 61 | 62 | V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \ | |
| 62 | 63 | V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \ | |
| 63 | - "MessagePort was found in message but not listed in transferList") | ||
| 64 | + "MessagePort was found in message but not listed in transferList") \ | ||
| 65 | + V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \ | ||
| 66 | + "Cannot serialize externalized SharedArrayBuffer") \ | ||
| 64 | 67 | ||
| 65 | 68 | #define V(code, message) \ | |
| 66 | 69 | inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,7 @@ using v8::Maybe; | |||
| 24 | 24 | using v8::MaybeLocal; | |
| 25 | 25 | using v8::Nothing; | |
| 26 | 26 | using v8::Object; | |
| 27 | + using v8::SharedArrayBuffer; | ||
| 27 | 28 | using v8::String; | |
| 28 | 29 | using v8::Value; | |
| 29 | 30 | using v8::ValueDeserializer; | |
@@ -43,8 +44,13 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { | |||
| 43 | 44 | public: | |
| 44 | 45 | DeserializerDelegate(Message* m, | |
| 45 | 46 | Environment* env, | |
| 46 | - const std::vector<MessagePort*>& message_ports) | ||
| 47 | - : env_(env), msg_(m), message_ports_(message_ports) {} | ||
| 47 | + const std::vector<MessagePort*>& message_ports, | ||
| 48 | + const std::vector<Local<SharedArrayBuffer>>& | ||
| 49 | + shared_array_buffers) | ||
| 50 | + : env_(env), | ||
| 51 | + msg_(m), | ||
| 52 | + message_ports_(message_ports), | ||
| 53 | + shared_array_buffers_(shared_array_buffers) {} | ||
| 48 | 54 | ||
| 49 | 55 | MaybeLocal<Object> ReadHostObject(Isolate* isolate) override { | |
| 50 | 56 | // Currently, only MessagePort hosts objects are supported, so identifying | |
@@ -56,12 +62,19 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { | |||
| 56 | 62 | return message_ports_[id]->object(); | |
| 57 | 63 | }; | |
| 58 | 64 | ||
| 65 | + MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId( | ||
| 66 | + Isolate* isolate, uint32_t clone_id) override { | ||
| 67 | + CHECK_LE(clone_id, shared_array_buffers_.size()); | ||
| 68 | + return shared_array_buffers_[clone_id]; | ||
| 69 | + } | ||
| 70 | + | ||
| 59 | 71 | ValueDeserializer* deserializer = nullptr; | |
| 60 | 72 | ||
| 61 | 73 | private: | |
| 62 | 74 | Environment* env_; | |
| 63 | 75 | Message* msg_; | |
| 64 | 76 | const std::vector<MessagePort*>& message_ports_; | |
| 77 | + const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_; | ||
| 65 | 78 | }; | |
| 66 | 79 | ||
| 67 | 80 | } // anonymous namespace | |
@@ -87,7 +100,18 @@ MaybeLocal<Value> Message::Deserialize(Environment* env, | |||
| 87 | 100 | } | |
| 88 | 101 | message_ports_.clear(); | |
| 89 | 102 | ||
| 90 | - DeserializerDelegate delegate(this, env, ports); | ||
| 103 | + std::vector<Local<SharedArrayBuffer>> shared_array_buffers; | ||
| 104 | + // Attach all transfered SharedArrayBuffers to their new Isolate. | ||
| 105 | + for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) { | ||
| 106 | + Local<SharedArrayBuffer> sab; | ||
| 107 | + if (!shared_array_buffers_[i]->GetSharedArrayBuffer(env, context) | ||
| 108 | + .ToLocal(&sab)) | ||
| 109 | + return MaybeLocal<Value>(); | ||
| 110 | + shared_array_buffers.push_back(sab); | ||
| 111 | + } | ||
| 112 | + shared_array_buffers_.clear(); | ||
| 113 | + | ||
| 114 | + DeserializerDelegate delegate(this, env, ports, shared_array_buffers); | ||
| 91 | 115 | ValueDeserializer deserializer( | |
| 92 | 116 | env->isolate(), | |
| 93 | 117 | reinterpret_cast<const uint8_t*>(main_message_buf_.data), | |
@@ -112,6 +136,11 @@ MaybeLocal<Value> Message::Deserialize(Environment* env, | |||
| 112 | 136 | deserializer.ReadValue(context).FromMaybe(Local<Value>())); | |
| 113 | 137 | } | |
| 114 | 138 | ||
| 139 | + void Message::AddSharedArrayBuffer( | ||
| 140 | + SharedArrayBufferMetadataReference reference) { | ||
| 141 | + shared_array_buffers_.push_back(reference); | ||
| 142 | + } | ||
| 143 | + | ||
| 115 | 144 | void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) { | |
| 116 | 145 | message_ports_.emplace_back(std::move(data)); | |
| 117 | 146 | } | |
@@ -139,6 +168,27 @@ class SerializerDelegate : public ValueSerializer::Delegate { | |||
| 139 | 168 | return Nothing<bool>(); | |
| 140 | 169 | } | |
| 141 | 170 | ||
| 171 | + Maybe<uint32_t> GetSharedArrayBufferId( | ||
| 172 | + Isolate* isolate, | ||
| 173 | + Local<SharedArrayBuffer> shared_array_buffer) override { | ||
| 174 | + uint32_t i; | ||
| 175 | + for (i = 0; i < seen_shared_array_buffers_.size(); ++i) { | ||
| 176 | + if (seen_shared_array_buffers_[i] == shared_array_buffer) | ||
| 177 | + return Just(i); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer( | ||
| 181 | + env_, | ||
| 182 | + context_, | ||
| 183 | + shared_array_buffer); | ||
| 184 | + if (!reference) { | ||
| 185 | + return Nothing<uint32_t>(); | ||
| 186 | + } | ||
| 187 | + seen_shared_array_buffers_.push_back(shared_array_buffer); | ||
| 188 | + msg_->AddSharedArrayBuffer(reference); | ||
| 189 | + return Just(i); | ||
| 190 | + } | ||
| 191 | + | ||
| 142 | 192 | void Finish() { | |
| 143 | 193 | // Only close the MessagePort handles and actually transfer them | |
| 144 | 194 | // once we know that serialization succeeded. | |
@@ -166,6 +216,7 @@ class SerializerDelegate : public ValueSerializer::Delegate { | |||
| 166 | 216 | Environment* env_; | |
| 167 | 217 | Local<Context> context_; | |
| 168 | 218 | Message* msg_; | |
| 219 | + std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_; | ||
| 169 | 220 | std::vector<MessagePort*> ports_; | |
| 170 | 221 | ||
| 171 | 222 | friend class worker::Message; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,8 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | 6 | #include "env.h" | |
| 7 | 7 | #include "node_mutex.h" | |
| 8 | + #include "sharedarraybuffer_metadata.h" | ||
| 8 | 9 | #include <list> | |
| 9 | - #include <memory> | ||
| 10 | 10 | ||
| 11 | 11 | namespace node { | |
| 12 | 12 | namespace worker { | |
@@ -37,13 +37,17 @@ class Message { | |||
| 37 | 37 | v8::Local<v8::Value> input, | |
| 38 | 38 | v8::Local<v8::Value> transfer_list); | |
| 39 | 39 | ||
| 40 | + // Internal method of Message that is called when a new SharedArrayBuffer | ||
| 41 | + // object is encountered in the incoming value's structure. | ||
| 42 | + void AddSharedArrayBuffer(SharedArrayBufferMetadataReference ref); | ||
| 40 | 43 | // Internal method of Message that is called once serialization finishes | |
| 41 | 44 | // and that transfers ownership of `data` to this message. | |
| 42 | 45 | void AddMessagePort(std::unique_ptr<MessagePortData>&& data); | |
| 43 | 46 | ||
| 44 | 47 | private: | |
| 45 | 48 | MallocedBuffer<char> main_message_buf_; | |
| 46 | 49 | std::vector<MallocedBuffer<char>> array_buffer_contents_; | |
| 50 | + std::vector<SharedArrayBufferMetadataReference> shared_array_buffers_; | ||
| 47 | 51 | std::vector<std::unique_ptr<MessagePortData>> message_ports_; | |
| 48 | 52 | ||
| 49 | 53 | friend class MessagePort; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,129 @@ | |||
| 1 | + #include "sharedarraybuffer_metadata.h" | ||
| 2 | + #include "base_object.h" | ||
| 3 | + #include "base_object-inl.h" | ||
| 4 | + #include "node_errors.h" | ||
| 5 | + | ||
| 6 | + using v8::Context; | ||
| 7 | + using v8::Function; | ||
| 8 | + using v8::FunctionTemplate; | ||
| 9 | + using v8::Local; | ||
| 10 | + using v8::Maybe; | ||
| 11 | + using v8::MaybeLocal; | ||
| 12 | + using v8::Nothing; | ||
| 13 | + using v8::Object; | ||
| 14 | + using v8::SharedArrayBuffer; | ||
| 15 | + using v8::Value; | ||
| 16 | + | ||
| 17 | + namespace node { | ||
| 18 | + namespace worker { | ||
| 19 | + | ||
| 20 | + namespace { | ||
| 21 | + | ||
| 22 | + // Yield a JS constructor for SABLifetimePartner objects in the form of a | ||
| 23 | + // standard API object, that has a single field for containing the raw | ||
| 24 | + // SABLiftimePartner* pointer. | ||
| 25 | + Local<Function> GetSABLifetimePartnerConstructor( | ||
| 26 | + Environment* env, Local<Context> context) { | ||
| 27 | + Local<FunctionTemplate> templ; | ||
| 28 | + templ = env->sab_lifetimepartner_constructor_template(); | ||
| 29 | + if (!templ.IsEmpty()) | ||
| 30 | + return templ->GetFunction(context).ToLocalChecked(); | ||
| 31 | + | ||
| 32 | + templ = BaseObject::MakeLazilyInitializedJSTemplate(env); | ||
| 33 | + templ->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), | ||
| 34 | + "SABLifetimePartner")); | ||
| 35 | + env->set_sab_lifetimepartner_constructor_template(templ); | ||
| 36 | + | ||
| 37 | + return GetSABLifetimePartnerConstructor(env, context); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + class SABLifetimePartner : public BaseObject { | ||
| 41 | + public: | ||
| 42 | + SABLifetimePartner(Environment* env, | ||
| 43 | + Local<Object> obj, | ||
| 44 | + SharedArrayBufferMetadataReference r) | ||
| 45 | + : BaseObject(env, obj), | ||
| 46 | + reference(r) { | ||
| 47 | + MakeWeak(); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + SharedArrayBufferMetadataReference reference; | ||
| 51 | + }; | ||
| 52 | + | ||
| 53 | + } // anonymous namespace | ||
| 54 | + | ||
| 55 | + SharedArrayBufferMetadataReference | ||
| 56 | + SharedArrayBufferMetadata::ForSharedArrayBuffer( | ||
| 57 | + Environment* env, | ||
| 58 | + Local<Context> context, | ||
| 59 | + Local<SharedArrayBuffer> source) { | ||
| 60 | + Local<Value> lifetime_partner; | ||
| 61 | + | ||
| 62 | + if (!source->GetPrivate(context, | ||
| 63 | + env->sab_lifetimepartner_symbol()) | ||
| 64 | + .ToLocal(&lifetime_partner)) { | ||
| 65 | + return nullptr; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + if (lifetime_partner->IsObject() && | ||
| 69 | + env->sab_lifetimepartner_constructor_template() | ||
| 70 | + ->HasInstance(lifetime_partner)) { | ||
| 71 | + CHECK(source->IsExternal()); | ||
| 72 | + SABLifetimePartner* partner = | ||
| 73 | + Unwrap<SABLifetimePartner>(lifetime_partner.As<Object>()); | ||
| 74 | + CHECK_NE(partner, nullptr); | ||
| 75 | + return partner->reference; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + if (source->IsExternal()) { | ||
| 79 | + // If this is an external SharedArrayBuffer but we do not see a lifetime | ||
| 80 | + // partner object, it was not us who externalized it. In that case, there | ||
| 81 | + // is no way to serialize it, because it's unclear how the memory | ||
| 82 | + // is actually owned. | ||
| 83 | + THROW_ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER(env); | ||
| 84 | + return nullptr; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + SharedArrayBuffer::Contents contents = source->Externalize(); | ||
| 88 | + SharedArrayBufferMetadataReference r(new SharedArrayBufferMetadata( | ||
| 89 | + contents.Data(), contents.ByteLength())); | ||
| 90 | + if (r->AssignToSharedArrayBuffer(env, context, source).IsNothing()) | ||
| 91 | + return nullptr; | ||
| 92 | + return r; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + Maybe<bool> SharedArrayBufferMetadata::AssignToSharedArrayBuffer( | ||
| 96 | + Environment* env, Local<Context> context, | ||
| 97 | + Local<SharedArrayBuffer> target) { | ||
| 98 | + CHECK(target->IsExternal()); | ||
| 99 | + Local<Function> ctor = GetSABLifetimePartnerConstructor(env, context); | ||
| 100 | + Local<Object> obj; | ||
| 101 | + if (!ctor->NewInstance(context).ToLocal(&obj)) | ||
| 102 | + return Nothing<bool>(); | ||
| 103 | + | ||
| 104 | + new SABLifetimePartner(env, obj, shared_from_this()); | ||
| 105 | + return target->SetPrivate(context, | ||
| 106 | + env->sab_lifetimepartner_symbol(), | ||
| 107 | + obj); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + SharedArrayBufferMetadata::SharedArrayBufferMetadata(void* data, size_t size) | ||
| 111 | + : data(data), size(size) { } | ||
| 112 | + | ||
| 113 | + SharedArrayBufferMetadata::~SharedArrayBufferMetadata() { | ||
| 114 | + free(data); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + MaybeLocal<SharedArrayBuffer> SharedArrayBufferMetadata::GetSharedArrayBuffer( | ||
| 118 | + Environment* env, Local<Context> context) { | ||
| 119 | + Local<SharedArrayBuffer> obj = | ||
| 120 | + SharedArrayBuffer::New(env->isolate(), data, size); | ||
| 121 | + | ||
| 122 | + if (AssignToSharedArrayBuffer(env, context, obj).IsNothing()) | ||
| 123 | + return MaybeLocal<SharedArrayBuffer>(); | ||
| 124 | + | ||
| 125 | + return obj; | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + } // namespace worker | ||
| 129 | + } // namespace node | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments