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() {
i::Handle<i::WasmModuleObject> obj =
i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this));
i::wasm::NativeModule* native_module = obj->native_module();
size_t buffer_size =
i::wasm::GetSerializedNativeModuleSize(obj->GetIsolate(), native_module);
i::wasm::WasmSerializer wasm_serializer(obj->GetIsolate(), native_module);
size_t buffer_size = wasm_serializer.GetSerializedNativeModuleSize();
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
if (i::wasm::SerializeNativeModule(obj->GetIsolate(), native_module,
{buffer.get(), buffer_size}))
if (wasm_serializer.SerializeNativeModule({buffer.get(), buffer_size}))
return {std::move(buffer), buffer_size};
return {};
}
......
......@@ -904,14 +904,13 @@ RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
wasm::NativeModule* native_module = module_obj->native_module();
size_t compiled_size =
wasm::GetSerializedNativeModuleSize(isolate, native_module);
wasm::WasmSerializer wasm_serializer(isolate, native_module);
size_t compiled_size = wasm_serializer.GetSerializedNativeModuleSize();
void* array_data = isolate->array_buffer_allocator()->Allocate(compiled_size);
Handle<JSArrayBuffer> array_buffer = isolate->factory()->NewJSArrayBuffer();
JSArrayBuffer::Setup(array_buffer, isolate, false, array_data, compiled_size);
if (!array_data ||
!wasm::SerializeNativeModule(
isolate, native_module,
!wasm_serializer.SerializeNativeModule(
{reinterpret_cast<uint8_t*>(array_data), compiled_size})) {
return ReadOnlyRoots(isolate).undefined_value();
}
......
......@@ -895,14 +895,13 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
memcpy(destination, wire_bytes.start(), wire_bytes.size());
}
size_t module_size =
wasm::GetSerializedNativeModuleSize(isolate_, native_module);
wasm::WasmSerializer wasm_serializer(isolate_, native_module);
size_t module_size = wasm_serializer.GetSerializedNativeModuleSize();
CHECK_GE(std::numeric_limits<uint32_t>::max(), module_size);
WriteVarint<uint32_t>(static_cast<uint32_t>(module_size));
uint8_t* module_buffer;
if (ReserveRawBytes(module_size).To(&module_buffer)) {
if (!wasm::SerializeNativeModule(isolate_, native_module,
{module_buffer, module_size})) {
if (!wasm_serializer.SerializeNativeModule({module_buffer, module_size})) {
return Nothing<bool>();
}
}
......
......@@ -588,6 +588,14 @@ void NativeModule::PublishCode(WasmCode* code) {
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) {
// Only call this if we really need a jump table.
DCHECK_LT(0, num_wasm_functions);
......
......@@ -269,6 +269,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
// threads executing the old 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 {
DCHECK_LT(index, num_functions());
DCHECK_LE(module_->num_imported_functions, index);
......@@ -322,9 +326,6 @@ class V8_EXPORT_PRIVATE NativeModule final {
uint32_t num_imported_functions() const {
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_; }
void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; }
bool lazy_compile_frozen() const { return lazy_compile_frozen_; }
......@@ -369,6 +370,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
void PatchJumpTable(uint32_t func_index, Address target,
WasmCode::FlushICache);
Vector<WasmCode*> code_table() const {
return {code_table_.get(), module_->num_declared_functions};
}
void set_code(uint32_t index, WasmCode* code) {
DCHECK_LT(index, num_functions());
DCHECK_LE(module_->num_imported_functions, index);
......
......@@ -211,7 +211,8 @@ constexpr size_t kCodeHeaderSize =
class V8_EXPORT_PRIVATE NativeModuleSerializer {
public:
NativeModuleSerializer() = delete;
NativeModuleSerializer(Isolate*, const NativeModule*);
NativeModuleSerializer(Isolate*, const NativeModule*,
Vector<WasmCode* const>);
size_t Measure() const;
bool Write(Writer* writer);
......@@ -223,6 +224,7 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer {
Isolate* const isolate_;
const NativeModule* const native_module_;
Vector<WasmCode* const> code_table_;
bool write_called_;
// Reverse lookup tables for embedded addresses.
......@@ -232,9 +234,13 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer {
DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer);
};
NativeModuleSerializer::NativeModuleSerializer(Isolate* isolate,
const NativeModule* module)
: isolate_(isolate), native_module_(module), write_called_(false) {
NativeModuleSerializer::NativeModuleSerializer(
Isolate* isolate, const NativeModule* module,
Vector<WasmCode* const> code_table)
: isolate_(isolate),
native_module_(module),
code_table_(code_table),
write_called_(false) {
DCHECK_NOT_NULL(isolate_);
DCHECK_NOT_NULL(native_module_);
// TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
......@@ -263,7 +269,7 @@ size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
size_t NativeModuleSerializer::Measure() const {
size_t size = kHeaderSize;
for (WasmCode* code : native_module_->code_table()) {
for (WasmCode* code : code_table_) {
size += MeasureCode(code);
}
return size;
......@@ -370,26 +376,31 @@ bool NativeModuleSerializer::Write(Writer* writer) {
WriteHeader(writer);
for (WasmCode* code : native_module_->code_table()) {
for (WasmCode* code : code_table_) {
WriteCode(code, writer);
}
return true;
}
size_t GetSerializedNativeModuleSize(Isolate* isolate,
NativeModule* native_module) {
NativeModuleSerializer serializer(isolate, native_module);
WasmSerializer::WasmSerializer(Isolate* isolate, NativeModule* native_module)
: isolate_(isolate),
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();
}
bool SerializeNativeModule(Isolate* isolate, NativeModule* native_module,
Vector<byte> buffer) {
NativeModuleSerializer serializer(isolate, native_module);
bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const {
Vector<WasmCode* const> code_table(code_table_.data(), code_table_.size());
NativeModuleSerializer serializer(isolate_, native_module_, code_table);
size_t measured_size = kVersionSize + serializer.Measure();
if (buffer.size() < measured_size) return false;
Writer writer(buffer);
WriteVersion(isolate, &writer);
WriteVersion(isolate_, &writer);
if (!serializer.Write(&writer)) return false;
DCHECK_EQ(measured_size, writer.bytes_written());
......
......@@ -11,12 +11,27 @@ namespace v8 {
namespace internal {
namespace wasm {
size_t GetSerializedNativeModuleSize(Isolate* isolate,
NativeModule* native_module);
bool SerializeNativeModule(Isolate* isolate, NativeModule* native_module,
Vector<byte> buffer);
// Support to serialize WebAssembly {NativeModule} objects. This class intends
// 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.
class WasmSerializer {
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(
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