// Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "test/wasm-api-tests/wasm-api-test.h" #include #include namespace v8 { namespace internal { namespace wasm { using ::wasm::Shared; namespace { const int kNumThreads = 10; const int kIterationsPerThread = 3; int g_traces; own Callback(void* env, const Val args[], Val results[]) { std::lock_guard lock(*reinterpret_cast(env)); g_traces += args[0].i32(); return nullptr; } void Main(Engine* engine, Shared* shared, std::mutex* mutex, int id) { own store = Store::make(engine); own module = Module::obtain(store.get(), shared); EXPECT_NE(nullptr, module.get()); for (int i = 0; i < kIterationsPerThread; i++) { std::this_thread::sleep_for(std::chrono::microseconds(100)); // Create imports. own func_type = FuncType::make(ownvec::make(ValType::make(::wasm::I32)), ownvec::make()); own func = Func::make(store.get(), func_type.get(), Callback, mutex); own<::wasm::GlobalType> global_type = ::wasm::GlobalType::make(ValType::make(::wasm::I32), ::wasm::CONST); own global = Global::make(store.get(), global_type.get(), Val::i32(id)); // Instantiate and run. // With the current implementation of the WasmModuleBuilder, global // imports always come before function imports, regardless of the // order of builder()->Add*Import() calls below. Extern* imports[] = {global.get(), func.get()}; own instance = Instance::make(store.get(), module.get(), imports); ownvec exports = instance->exports(); Func* run_func = exports[0]->func(); run_func->call(); } } } // namespace TEST_F(WasmCapiTest, Threads) { // Create module. ValueType i32_type[] = {kWasmI32}; FunctionSig param_i32(0, 1, i32_type); uint32_t callback_index = builder()->AddImport(CStrVector("callback"), ¶m_i32); uint32_t global_index = builder()->AddGlobalImport(CStrVector("id"), kWasmI32, false); byte code[] = { WASM_CALL_FUNCTION(callback_index, WASM_GET_GLOBAL(global_index))}; FunctionSig empty_sig(0, 0, nullptr); AddExportedFunction(CStrVector("run"), code, sizeof(code), &empty_sig); Compile(); own> shared = module()->share(); // Spawn threads. g_traces = 0; std::mutex mutex; std::thread threads[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { threads[i] = std::thread(Main, engine(), shared.get(), &mutex, i); } for (int i = 0; i < kNumThreads; i++) { threads[i].join(); } // Each thread in each iteration adds its ID to {traces}, so in the end // we expect kIterationsPerThread * sum([0, ..., kNumThreads-1]). // Per Gauss: const int kExpected = kIterationsPerThread * (kNumThreads - 1) * kNumThreads / 2; EXPECT_EQ(kExpected, g_traces); } TEST_F(WasmCapiTest, MultiStoresOneThread) { // These Stores intentionally have overlapping, but non-nested lifetimes. own store1 = Store::make(engine()); own store2 = Store::make(engine()); own store3 = Store::make(engine()); store1.reset(); store2.reset(); store3.reset(); } } // namespace wasm } // namespace internal } // namespace v8