Commit c90dd37e authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Make native module serialization thread safe.

This changes the WebAssembly serializer to take a full snapshot of the
code table before measuring and serializing the module. It allows other
threads (or other Isolates) to mutate the native module while the main
thread is serializing the module.

R=clemensh@chromium.org
BUG=v8:7424

Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I64bc27600452880f37f4fbb2f40c77c79975358f
Reviewed-on: https://chromium-review.googlesource.com/1156596
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54838}
parent c3a378dd
...@@ -7694,11 +7694,10 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() { ...@@ -7694,11 +7694,10 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::WasmModuleObject> obj = i::Handle<i::WasmModuleObject> obj =
i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this)); i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this));
i::wasm::NativeModule* native_module = obj->native_module(); i::wasm::NativeModule* native_module = obj->native_module();
size_t buffer_size = i::wasm::WasmSerializer wasm_serializer(obj->GetIsolate(), native_module);
i::wasm::GetSerializedNativeModuleSize(obj->GetIsolate(), native_module); size_t buffer_size = wasm_serializer.GetSerializedNativeModuleSize();
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
if (i::wasm::SerializeNativeModule(obj->GetIsolate(), native_module, if (wasm_serializer.SerializeNativeModule({buffer.get(), buffer_size}))
{buffer.get(), buffer_size}))
return {std::move(buffer), buffer_size}; return {std::move(buffer), buffer_size};
return {}; return {};
} }
......
...@@ -904,14 +904,13 @@ RUNTIME_FUNCTION(Runtime_SerializeWasmModule) { ...@@ -904,14 +904,13 @@ RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0); CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
wasm::NativeModule* native_module = module_obj->native_module(); wasm::NativeModule* native_module = module_obj->native_module();
size_t compiled_size = wasm::WasmSerializer wasm_serializer(isolate, native_module);
wasm::GetSerializedNativeModuleSize(isolate, native_module); size_t compiled_size = wasm_serializer.GetSerializedNativeModuleSize();
void* array_data = isolate->array_buffer_allocator()->Allocate(compiled_size); void* array_data = isolate->array_buffer_allocator()->Allocate(compiled_size);
Handle<JSArrayBuffer> array_buffer = isolate->factory()->NewJSArrayBuffer(); Handle<JSArrayBuffer> array_buffer = isolate->factory()->NewJSArrayBuffer();
JSArrayBuffer::Setup(array_buffer, isolate, false, array_data, compiled_size); JSArrayBuffer::Setup(array_buffer, isolate, false, array_data, compiled_size);
if (!array_data || if (!array_data ||
!wasm::SerializeNativeModule( !wasm_serializer.SerializeNativeModule(
isolate, native_module,
{reinterpret_cast<uint8_t*>(array_data), compiled_size})) { {reinterpret_cast<uint8_t*>(array_data), compiled_size})) {
return ReadOnlyRoots(isolate).undefined_value(); return ReadOnlyRoots(isolate).undefined_value();
} }
......
...@@ -895,14 +895,13 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) { ...@@ -895,14 +895,13 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
memcpy(destination, wire_bytes.start(), wire_bytes.size()); memcpy(destination, wire_bytes.start(), wire_bytes.size());
} }
size_t module_size = wasm::WasmSerializer wasm_serializer(isolate_, native_module);
wasm::GetSerializedNativeModuleSize(isolate_, native_module); size_t module_size = wasm_serializer.GetSerializedNativeModuleSize();
CHECK_GE(std::numeric_limits<uint32_t>::max(), module_size); CHECK_GE(std::numeric_limits<uint32_t>::max(), module_size);
WriteVarint<uint32_t>(static_cast<uint32_t>(module_size)); WriteVarint<uint32_t>(static_cast<uint32_t>(module_size));
uint8_t* module_buffer; uint8_t* module_buffer;
if (ReserveRawBytes(module_size).To(&module_buffer)) { if (ReserveRawBytes(module_size).To(&module_buffer)) {
if (!wasm::SerializeNativeModule(isolate_, native_module, if (!wasm_serializer.SerializeNativeModule({module_buffer, module_size})) {
{module_buffer, module_size})) {
return Nothing<bool>(); return Nothing<bool>();
} }
} }
......
...@@ -588,6 +588,14 @@ void NativeModule::PublishCode(WasmCode* code) { ...@@ -588,6 +588,14 @@ void NativeModule::PublishCode(WasmCode* code) {
WasmCode::kFlushICache); WasmCode::kFlushICache);
} }
std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const {
base::LockGuard<base::Mutex> lock(&allocation_mutex_);
std::vector<WasmCode*> result;
result.reserve(code_table().size());
for (wasm::WasmCode* code : code_table()) result.push_back(code);
return result;
}
WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t num_wasm_functions) { WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t num_wasm_functions) {
// Only call this if we really need a jump table. // Only call this if we really need a jump table.
DCHECK_LT(0, num_wasm_functions); DCHECK_LT(0, num_wasm_functions);
......
...@@ -269,6 +269,10 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -269,6 +269,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
// threads executing the old code. // threads executing the old code.
void PublishCode(WasmCode* code); void PublishCode(WasmCode* code);
// Creates a snapshot of the current state of the code table. This is useful
// to get a consistent view of the table (e.g. used by the serializer).
std::vector<WasmCode*> SnapshotCodeTable() const;
WasmCode* code(uint32_t index) const { WasmCode* code(uint32_t index) const {
DCHECK_LT(index, num_functions()); DCHECK_LT(index, num_functions());
DCHECK_LE(module_->num_imported_functions, index); DCHECK_LE(module_->num_imported_functions, index);
...@@ -322,9 +326,6 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -322,9 +326,6 @@ class V8_EXPORT_PRIVATE NativeModule final {
uint32_t num_imported_functions() const { uint32_t num_imported_functions() const {
return module_->num_imported_functions; return module_->num_imported_functions;
} }
Vector<WasmCode*> code_table() const {
return {code_table_.get(), module_->num_declared_functions};
}
bool use_trap_handler() const { return use_trap_handler_; } bool use_trap_handler() const { return use_trap_handler_; }
void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; } void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; }
bool lazy_compile_frozen() const { return lazy_compile_frozen_; } bool lazy_compile_frozen() const { return lazy_compile_frozen_; }
...@@ -369,6 +370,9 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -369,6 +370,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
void PatchJumpTable(uint32_t func_index, Address target, void PatchJumpTable(uint32_t func_index, Address target,
WasmCode::FlushICache); WasmCode::FlushICache);
Vector<WasmCode*> code_table() const {
return {code_table_.get(), module_->num_declared_functions};
}
void set_code(uint32_t index, WasmCode* code) { void set_code(uint32_t index, WasmCode* code) {
DCHECK_LT(index, num_functions()); DCHECK_LT(index, num_functions());
DCHECK_LE(module_->num_imported_functions, index); DCHECK_LE(module_->num_imported_functions, index);
......
...@@ -211,7 +211,8 @@ constexpr size_t kCodeHeaderSize = ...@@ -211,7 +211,8 @@ constexpr size_t kCodeHeaderSize =
class V8_EXPORT_PRIVATE NativeModuleSerializer { class V8_EXPORT_PRIVATE NativeModuleSerializer {
public: public:
NativeModuleSerializer() = delete; NativeModuleSerializer() = delete;
NativeModuleSerializer(Isolate*, const NativeModule*); NativeModuleSerializer(Isolate*, const NativeModule*,
Vector<WasmCode* const>);
size_t Measure() const; size_t Measure() const;
bool Write(Writer* writer); bool Write(Writer* writer);
...@@ -223,6 +224,7 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer { ...@@ -223,6 +224,7 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer {
Isolate* const isolate_; Isolate* const isolate_;
const NativeModule* const native_module_; const NativeModule* const native_module_;
Vector<WasmCode* const> code_table_;
bool write_called_; bool write_called_;
// Reverse lookup tables for embedded addresses. // Reverse lookup tables for embedded addresses.
...@@ -232,9 +234,13 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer { ...@@ -232,9 +234,13 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer {
DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer); DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer);
}; };
NativeModuleSerializer::NativeModuleSerializer(Isolate* isolate, NativeModuleSerializer::NativeModuleSerializer(
const NativeModule* module) Isolate* isolate, const NativeModule* module,
: isolate_(isolate), native_module_(module), write_called_(false) { Vector<WasmCode* const> code_table)
: isolate_(isolate),
native_module_(module),
code_table_(code_table),
write_called_(false) {
DCHECK_NOT_NULL(isolate_); DCHECK_NOT_NULL(isolate_);
DCHECK_NOT_NULL(native_module_); DCHECK_NOT_NULL(native_module_);
// TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist // TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
...@@ -263,7 +269,7 @@ size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const { ...@@ -263,7 +269,7 @@ size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
size_t NativeModuleSerializer::Measure() const { size_t NativeModuleSerializer::Measure() const {
size_t size = kHeaderSize; size_t size = kHeaderSize;
for (WasmCode* code : native_module_->code_table()) { for (WasmCode* code : code_table_) {
size += MeasureCode(code); size += MeasureCode(code);
} }
return size; return size;
...@@ -370,26 +376,31 @@ bool NativeModuleSerializer::Write(Writer* writer) { ...@@ -370,26 +376,31 @@ bool NativeModuleSerializer::Write(Writer* writer) {
WriteHeader(writer); WriteHeader(writer);
for (WasmCode* code : native_module_->code_table()) { for (WasmCode* code : code_table_) {
WriteCode(code, writer); WriteCode(code, writer);
} }
return true; return true;
} }
size_t GetSerializedNativeModuleSize(Isolate* isolate, WasmSerializer::WasmSerializer(Isolate* isolate, NativeModule* native_module)
NativeModule* native_module) { : isolate_(isolate),
NativeModuleSerializer serializer(isolate, native_module); native_module_(native_module),
code_table_(native_module->SnapshotCodeTable()) {}
size_t WasmSerializer::GetSerializedNativeModuleSize() const {
Vector<WasmCode* const> code_table(code_table_.data(), code_table_.size());
NativeModuleSerializer serializer(isolate_, native_module_, code_table);
return kVersionSize + serializer.Measure(); return kVersionSize + serializer.Measure();
} }
bool SerializeNativeModule(Isolate* isolate, NativeModule* native_module, bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const {
Vector<byte> buffer) { Vector<WasmCode* const> code_table(code_table_.data(), code_table_.size());
NativeModuleSerializer serializer(isolate, native_module); NativeModuleSerializer serializer(isolate_, native_module_, code_table);
size_t measured_size = kVersionSize + serializer.Measure(); size_t measured_size = kVersionSize + serializer.Measure();
if (buffer.size() < measured_size) return false; if (buffer.size() < measured_size) return false;
Writer writer(buffer); Writer writer(buffer);
WriteVersion(isolate, &writer); WriteVersion(isolate_, &writer);
if (!serializer.Write(&writer)) return false; if (!serializer.Write(&writer)) return false;
DCHECK_EQ(measured_size, writer.bytes_written()); DCHECK_EQ(measured_size, writer.bytes_written());
......
...@@ -11,12 +11,27 @@ namespace v8 { ...@@ -11,12 +11,27 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
size_t GetSerializedNativeModuleSize(Isolate* isolate, // Support to serialize WebAssembly {NativeModule} objects. This class intends
NativeModule* native_module); // to be thread-safe in that it takes a consistent snapshot of the module state
// at instantiation, allowing other threads to mutate the module concurrently.
bool SerializeNativeModule(Isolate* isolate, NativeModule* native_module, class WasmSerializer {
Vector<byte> buffer); public:
WasmSerializer(Isolate* isolate, NativeModule* native_module);
// Measure the required buffer size needed for serialization.
size_t GetSerializedNativeModuleSize() const;
// Serialize the {NativeModule} into the provided {buffer}. Returns true on
// success and false if the given buffer it too small for serialization.
bool SerializeNativeModule(Vector<byte> buffer) const;
private:
Isolate* isolate_;
NativeModule* native_module_;
std::vector<WasmCode*> code_table_;
};
// Support to deserialize WebAssembly {NativeModule} objects.
MaybeHandle<WasmModuleObject> DeserializeNativeModule( MaybeHandle<WasmModuleObject> DeserializeNativeModule(
Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes); Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment