[wasm] Keep NativeModule alive in BackgroundCompileScope

We need to ensure that the NativeModule stays alive while any
{BackgroundCompileScope} exists, because during that time we hold
shared ownership of the mutex in the {BackgroundCompileToken}. If the
{NativeModule} dies during that period, we would need to get exclusive
ownership of the mutex and deadlock.

This change requires holding a {std::weak_ptr<NativeModule>} in the
BackgroundCompileToken instead of a raw pointer, hence it can only be
initialized after the NativeModule was created. This is done via a
separate {InitCompilationState} method.

R=ahaas@chromium.org

Bug: v8:8979
Change-Id: Ia14bd272ea0bc47aec547024da6020608418c9d2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1518178
Auto-Submit: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60203}
This commit is contained in:
Clemens Hammacher 2019-03-12 17:10:58 +01:00 committed by Commit Bot
parent d358cf098c
commit 3df442d738
11 changed files with 55 additions and 37 deletions

View File

@ -126,8 +126,11 @@ class CompilationState {
friend class WasmCompilationUnit;
CompilationState() = delete;
static std::unique_ptr<CompilationState> New(NativeModule*,
std::shared_ptr<Counters>);
// The CompilationState keeps a {std::weak_ptr} back to the {NativeModule}
// such that it can keep it alive (by regaining a {std::shared_ptr}) in
// certain scopes.
static std::unique_ptr<CompilationState> New(
const std::shared_ptr<NativeModule>&, std::shared_ptr<Counters>);
};
} // namespace wasm

View File

@ -61,22 +61,23 @@ enum class CompileMode : uint8_t { kRegular, kTiering };
// on background compile jobs.
class BackgroundCompileToken {
public:
explicit BackgroundCompileToken(NativeModule* native_module)
explicit BackgroundCompileToken(
const std::shared_ptr<NativeModule>& native_module)
: native_module_(native_module) {}
void Cancel() {
base::SharedMutexGuard<base::kExclusive> mutex_guard(&mutex_);
native_module_ = nullptr;
native_module_.reset();
}
private:
friend class BackgroundCompileScope;
base::SharedMutex mutex_;
NativeModule* native_module_;
std::weak_ptr<NativeModule> native_module_;
NativeModule* StartScope() {
std::shared_ptr<NativeModule> StartScope() {
mutex_.LockShared();
return native_module_;
return native_module_.lock();
}
void ExitScope() { mutex_.UnlockShared(); }
@ -99,14 +100,15 @@ class BackgroundCompileScope {
NativeModule* native_module() {
DCHECK(!cancelled());
return native_module_;
return native_module_.get();
}
inline CompilationStateImpl* compilation_state();
private:
BackgroundCompileToken* const token_;
NativeModule* const native_module_;
// Keep the native module alive while in this scope.
std::shared_ptr<NativeModule> const native_module_;
};
// The {CompilationStateImpl} keeps track of the compilation state of the
@ -116,7 +118,8 @@ class BackgroundCompileScope {
// It's public interface {CompilationState} lives in compilation-environment.h.
class CompilationStateImpl {
public:
CompilationStateImpl(NativeModule*, std::shared_ptr<Counters> async_counters);
CompilationStateImpl(const std::shared_ptr<NativeModule>& native_module,
std::shared_ptr<Counters> async_counters);
~CompilationStateImpl();
// Cancel all background compilation and wait for all tasks to finish. Call
@ -310,7 +313,8 @@ void CompilationState::OnFinishedUnit(ExecutionTier tier, WasmCode* code) {
// static
std::unique_ptr<CompilationState> CompilationState::New(
NativeModule* native_module, std::shared_ptr<Counters> async_counters) {
const std::shared_ptr<NativeModule>& native_module,
std::shared_ptr<Counters> async_counters) {
return std::unique_ptr<CompilationState>(reinterpret_cast<CompilationState*>(
new CompilationStateImpl(native_module, std::move(async_counters))));
}
@ -726,7 +730,7 @@ class BackgroundCompileTask : public CancelableTask {
} // namespace
std::unique_ptr<NativeModule> CompileToNativeModule(
std::shared_ptr<NativeModule> CompileToNativeModule(
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
Handle<FixedArray>* export_wrappers_out) {
@ -1485,8 +1489,9 @@ bool AsyncStreamingProcessor::Deserialize(Vector<const uint8_t> module_bytes,
}
CompilationStateImpl::CompilationStateImpl(
NativeModule* native_module, std::shared_ptr<Counters> async_counters)
: native_module_(native_module),
const std::shared_ptr<NativeModule>& native_module,
std::shared_ptr<Counters> async_counters)
: native_module_(native_module.get()),
background_compile_token_(
std::make_shared<BackgroundCompileToken>(native_module)),
compile_mode_(FLAG_wasm_tier_up &&

View File

@ -37,7 +37,7 @@ class NativeModule;
class WasmCode;
struct WasmModule;
std::unique_ptr<NativeModule> CompileToNativeModule(
std::shared_ptr<NativeModule> CompileToNativeModule(
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
Handle<FixedArray>* export_wrappers_out);

View File

@ -368,11 +368,10 @@ WasmCode::~WasmCode() {
NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
bool can_request_more, VirtualMemory code_space,
std::shared_ptr<const WasmModule> module,
std::shared_ptr<Counters> async_counters)
std::shared_ptr<Counters> async_counters,
std::shared_ptr<NativeModule>* shared_this)
: enabled_features_(enabled),
module_(std::move(module)),
compilation_state_(
CompilationState::New(this, std::move(async_counters))),
import_wrapper_cache_(std::unique_ptr<WasmImportWrapperCache>(
new WasmImportWrapperCache(this))),
free_code_space_(code_space.region()),
@ -380,6 +379,13 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
can_request_more_memory_(can_request_more),
use_trap_handler_(trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler
: kNoTrapHandler) {
// We receive a pointer to an empty {std::shared_ptr}, and install ourselve
// there.
DCHECK_NOT_NULL(shared_this);
DCHECK_NULL(*shared_this);
shared_this->reset(this);
compilation_state_ =
CompilationState::New(*shared_this, std::move(async_counters));
DCHECK_NOT_NULL(module_);
owned_code_space_.emplace_back(std::move(code_space));
owned_code_.reserve(num_functions());
@ -1075,7 +1081,7 @@ size_t WasmCodeManager::EstimateNativeModuleNonCodeSize(
return wasm_module_estimate + native_module_estimate;
}
std::unique_ptr<NativeModule> WasmCodeManager::NewNativeModule(
std::shared_ptr<NativeModule> WasmCodeManager::NewNativeModule(
WasmEngine* engine, Isolate* isolate, const WasmFeatures& enabled,
size_t code_size_estimate, bool can_request_more,
std::shared_ptr<const WasmModule> module) {
@ -1111,9 +1117,11 @@ std::unique_ptr<NativeModule> WasmCodeManager::NewNativeModule(
Address start = code_space.address();
size_t size = code_space.size();
Address end = code_space.end();
std::unique_ptr<NativeModule> ret(
new NativeModule(engine, enabled, can_request_more, std::move(code_space),
std::move(module), isolate->async_counters()));
std::shared_ptr<NativeModule> ret;
new NativeModule(engine, enabled, can_request_more, std::move(code_space),
std::move(module), isolate->async_counters(), &ret);
// The constructor initialized the shared_ptr.
DCHECK_NOT_NULL(ret);
TRACE_HEAP("New NativeModule %p: Mem: %" PRIuPTR ",+%zu\n", ret.get(), start,
size);
base::MutexGuard lock(&native_modules_mutex_);

View File

@ -371,10 +371,12 @@ class V8_EXPORT_PRIVATE NativeModule final {
friend class WasmCodeManager;
friend class NativeModuleModificationScope;
// Private constructor, called via {WasmCodeManager::NewNativeModule()}.
NativeModule(WasmEngine* engine, const WasmFeatures& enabled_features,
bool can_request_more, VirtualMemory code_space,
std::shared_ptr<const WasmModule> module,
std::shared_ptr<Counters> async_counters);
std::shared_ptr<Counters> async_counters,
std::shared_ptr<NativeModule>* shared_this);
WasmCode* AddAnonymousCode(Handle<Code>, WasmCode::Kind kind,
const char* name = nullptr);
@ -518,7 +520,7 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
friend class NativeModule;
friend class WasmEngine;
std::unique_ptr<NativeModule> NewNativeModule(
std::shared_ptr<NativeModule> NewNativeModule(
WasmEngine* engine, Isolate* isolate,
const WasmFeatures& enabled_features, size_t code_size_estimate,
bool can_request_more, std::shared_ptr<const WasmModule> module);

View File

@ -140,7 +140,7 @@ MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
// Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
// in {CompileToNativeModule}.
Handle<FixedArray> export_wrappers;
std::unique_ptr<NativeModule> native_module =
std::shared_ptr<NativeModule> native_module =
CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
std::move(result).value(), bytes, &export_wrappers);
if (!native_module) return {};
@ -188,7 +188,7 @@ MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
// Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
// in {CompileToModuleObject}.
Handle<FixedArray> export_wrappers;
std::unique_ptr<NativeModule> native_module =
std::shared_ptr<NativeModule> native_module =
CompileToNativeModule(isolate, enabled, thrower,
std::move(result).value(), bytes, &export_wrappers);
if (!native_module) return {};
@ -487,10 +487,10 @@ void WasmEngine::EnableCodeLogging(Isolate* isolate) {
it->second->log_codes = true;
}
std::unique_ptr<NativeModule> WasmEngine::NewNativeModule(
std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
bool can_request_more, std::shared_ptr<const WasmModule> module) {
std::unique_ptr<NativeModule> native_module =
std::shared_ptr<NativeModule> native_module =
code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
can_request_more, std::move(module));
base::MutexGuard lock(&mutex_);

View File

@ -169,7 +169,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
// is determined with a heuristic based on the total size of wasm
// code. The native module may later request more memory.
// TODO(titzer): isolate is only required here for CompilationState.
std::unique_ptr<NativeModule> NewNativeModule(
std::shared_ptr<NativeModule> NewNativeModule(
Isolate* isolate, const WasmFeatures& enabled_features,
size_t code_size_estimate, bool can_request_more,
std::shared_ptr<const WasmModule> module);

View File

@ -118,7 +118,7 @@ Node* ToInt32(RawMachineAssembler& m, MachineType type, Node* a) {
}
}
std::unique_ptr<wasm::NativeModule> AllocateNativeModule(Isolate* isolate,
std::shared_ptr<wasm::NativeModule> AllocateNativeModule(Isolate* isolate,
size_t code_size) {
std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule());
module->num_declared_functions = 1;
@ -183,7 +183,7 @@ void TestReturnMultipleValues(MachineType type) {
if (i % 4 == 0) sign = -sign;
}
std::unique_ptr<wasm::NativeModule> module = AllocateNativeModule(
std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
handles.main_isolate(), code->raw_instruction_size());
byte* code_start =
module->AddCodeForTesting(code)->instructions().start();
@ -272,7 +272,7 @@ void ReturnLastValue(MachineType type) {
AssemblerOptions::Default(handles.main_isolate()), m.Export())
.ToHandleChecked();
std::unique_ptr<wasm::NativeModule> module = AllocateNativeModule(
std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
handles.main_isolate(), code->raw_instruction_size());
byte* code_start = module->AddCodeForTesting(code)->instructions().start();
@ -333,7 +333,7 @@ void ReturnSumOfReturns(MachineType type) {
AssemblerOptions::Default(handles.main_isolate()), m.Export())
.ToHandleChecked();
std::unique_ptr<wasm::NativeModule> module = AllocateNativeModule(
std::shared_ptr<wasm::NativeModule> module = AllocateNativeModule(
handles.main_isolate(), code->raw_instruction_size());
byte* code_start = module->AddCodeForTesting(code)->instructions().start();

View File

@ -17,7 +17,7 @@ namespace internal {
namespace wasm {
namespace test_wasm_import_wrapper_cache {
std::unique_ptr<NativeModule> NewModule(Isolate* isolate) {
std::shared_ptr<NativeModule> NewModule(Isolate* isolate) {
std::shared_ptr<WasmModule> module(new WasmModule);
bool can_request_more = false;
size_t size = 16384;

View File

@ -134,7 +134,7 @@ CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count,
return compiler::GetWasmCallDescriptor(zone, builder.Build());
}
std::unique_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
std::shared_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
size_t code_size) {
std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule);
module->num_declared_functions = 1;
@ -243,7 +243,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
AssemblerOptions::Default(i_isolate), callee.Export())
.ToHandleChecked();
std::unique_ptr<wasm::NativeModule> module =
std::shared_ptr<wasm::NativeModule> module =
AllocateNativeModule(i_isolate, code->raw_instruction_size());
byte* code_start = module->AddCodeForTesting(code)->instructions().start();
// Generate wrapper.

View File

@ -159,7 +159,7 @@ class WasmCodeManagerTest : public TestWithContext,
static constexpr uint32_t kJumpTableSize = RoundUp<kCodeAlignment>(
JumpTableAssembler::SizeForNumberOfSlots(kNumFunctions));
using NativeModulePtr = std::unique_ptr<NativeModule>;
using NativeModulePtr = std::shared_ptr<NativeModule>;
NativeModulePtr AllocModule(size_t size, ModuleStyle style) {
std::shared_ptr<WasmModule> module(new WasmModule);