v8/test/unittests/js-atomics/js-atomics-synchronization-primitive-unittest.cc
Shu-yu Guo 31e17fe62d [shared-struct, api] Support shared isolates in API
Currently the ability to create shared isolates is partially exposed to
API. Instead of fully exposing it, this CL makes shared isolate and
shared heap handling transparent to the embedder.

If a flag that requires the shared heap is true (currently
--shared-string-table and --harmony-struct), the first isolate created
in the process will create and attach to a process-wide shared isolate.
Subsequent isolates will attach to that shared isolate. When that first isolate is deleted, the shared isolate is also deleted.

Bug: v8:12547
Change-Id: Idaf2947bc354066c44f2d10243e10162b1b7e4d6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3848825
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Owners-Override: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82756}
2022-08-26 23:41:57 +00:00

215 lines
6.7 KiB
C++

// Copyright 2022 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 "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/heap/parked-scope.h"
#include "src/objects/js-atomics-synchronization-inl.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#if V8_CAN_CREATE_SHARED_HEAP_BOOL
namespace v8 {
namespace internal {
using JSAtomicsMutexTest = TestJSSharedMemoryWithNativeContext;
using JSAtomicsConditionTest = TestJSSharedMemoryWithNativeContext;
namespace {
class IsolateWithContextWrapper final {
public:
IsolateWithContextWrapper()
: isolate_wrapper_(kNoCounters),
isolate_scope_(isolate_wrapper_.isolate()),
handle_scope_(isolate_wrapper_.isolate()),
context_(v8::Context::New(isolate_wrapper_.isolate())),
context_scope_(context_) {}
v8::Isolate* v8_isolate() const { return isolate_wrapper_.isolate(); }
Isolate* isolate() const { return reinterpret_cast<Isolate*>(v8_isolate()); }
private:
IsolateWrapper isolate_wrapper_;
v8::Isolate::Scope isolate_scope_;
v8::HandleScope handle_scope_;
v8::Local<v8::Context> context_;
v8::Context::Scope context_scope_;
};
class LockingThread final : public ParkingThread {
public:
LockingThread(Handle<JSAtomicsMutex> mutex, ParkingSemaphore* sema_ready,
ParkingSemaphore* sema_execute_start,
ParkingSemaphore* sema_execute_complete)
: ParkingThread(Options("LockingThread")),
mutex_(mutex),
sema_ready_(sema_ready),
sema_execute_start_(sema_execute_start),
sema_execute_complete_(sema_execute_complete) {}
void Run() override {
IsolateWithContextWrapper isolate_wrapper;
Isolate* isolate = isolate_wrapper.isolate();
sema_ready_->Signal();
sema_execute_start_->ParkedWait(isolate->main_thread_local_isolate());
HandleScope scope(isolate);
JSAtomicsMutex::Lock(isolate, mutex_);
EXPECT_TRUE(mutex_->IsHeld());
EXPECT_TRUE(mutex_->IsCurrentThreadOwner());
base::OS::Sleep(base::TimeDelta::FromMilliseconds(1));
mutex_->Unlock(isolate);
sema_execute_complete_->Signal();
}
private:
Handle<JSAtomicsMutex> mutex_;
ParkingSemaphore* sema_ready_;
ParkingSemaphore* sema_execute_start_;
ParkingSemaphore* sema_execute_complete_;
};
} // namespace
TEST_F(JSAtomicsMutexTest, Contention) {
constexpr int kThreads = 32;
Isolate* i_main_isolate = i_isolate();
Handle<JSAtomicsMutex> contended_mutex =
i_main_isolate->factory()->NewJSAtomicsMutex();
ParkingSemaphore sema_ready(0);
ParkingSemaphore sema_execute_start(0);
ParkingSemaphore sema_execute_complete(0);
std::vector<std::unique_ptr<LockingThread>> threads;
for (int i = 0; i < kThreads; i++) {
auto thread = std::make_unique<LockingThread>(contended_mutex, &sema_ready,
&sema_execute_start,
&sema_execute_complete);
CHECK(thread->Start());
threads.push_back(std::move(thread));
}
LocalIsolate* local_isolate = i_main_isolate->main_thread_local_isolate();
for (int i = 0; i < kThreads; i++) {
sema_ready.ParkedWait(local_isolate);
}
for (int i = 0; i < kThreads; i++) sema_execute_start.Signal();
for (int i = 0; i < kThreads; i++) {
sema_execute_complete.ParkedWait(local_isolate);
}
ParkedScope parked(local_isolate);
for (auto& thread : threads) {
thread->ParkedJoin(parked);
}
EXPECT_FALSE(contended_mutex->IsHeld());
}
namespace {
class WaitOnConditionThread final : public ParkingThread {
public:
WaitOnConditionThread(Handle<JSAtomicsMutex> mutex,
Handle<JSAtomicsCondition> condition,
uint32_t* waiting_threads_count,
ParkingSemaphore* sema_ready,
ParkingSemaphore* sema_execute_complete)
: ParkingThread(Options("WaitOnConditionThread")),
mutex_(mutex),
condition_(condition),
waiting_threads_count_(waiting_threads_count),
sema_ready_(sema_ready),
sema_execute_complete_(sema_execute_complete) {}
void Run() override {
IsolateWithContextWrapper isolate_wrapper;
Isolate* isolate = isolate_wrapper.isolate();
sema_ready_->Signal();
HandleScope scope(isolate);
JSAtomicsMutex::Lock(isolate, mutex_);
while (keep_waiting) {
(*waiting_threads_count_)++;
EXPECT_TRUE(JSAtomicsCondition::WaitFor(isolate, condition_, mutex_,
base::nullopt));
(*waiting_threads_count_)--;
}
mutex_->Unlock(isolate);
sema_execute_complete_->Signal();
}
bool keep_waiting = true;
private:
Handle<JSAtomicsMutex> mutex_;
Handle<JSAtomicsCondition> condition_;
uint32_t* waiting_threads_count_;
ParkingSemaphore* sema_ready_;
ParkingSemaphore* sema_execute_complete_;
};
} // namespace
TEST_F(JSAtomicsConditionTest, NotifyAll) {
constexpr uint32_t kThreads = 32;
Isolate* i_main_isolate = i_isolate();
Handle<JSAtomicsMutex> mutex = i_main_isolate->factory()->NewJSAtomicsMutex();
Handle<JSAtomicsCondition> condition =
i_main_isolate->factory()->NewJSAtomicsCondition();
uint32_t waiting_threads_count = 0;
ParkingSemaphore sema_ready(0);
ParkingSemaphore sema_execute_complete(0);
std::vector<std::unique_ptr<WaitOnConditionThread>> threads;
for (uint32_t i = 0; i < kThreads; i++) {
auto thread = std::make_unique<WaitOnConditionThread>(
mutex, condition, &waiting_threads_count, &sema_ready,
&sema_execute_complete);
CHECK(thread->Start());
threads.push_back(std::move(thread));
}
LocalIsolate* local_isolate = i_main_isolate->main_thread_local_isolate();
for (uint32_t i = 0; i < kThreads; i++) {
sema_ready.ParkedWait(local_isolate);
}
// Wait until all threads are waiting on the condition.
for (;;) {
JSAtomicsMutex::LockGuard lock_guard(i_main_isolate, mutex);
uint32_t count = waiting_threads_count;
if (count == kThreads) break;
}
// Wake all the threads up.
for (uint32_t i = 0; i < kThreads; i++) {
threads[i]->keep_waiting = false;
}
EXPECT_EQ(kThreads,
condition->Notify(i_main_isolate, JSAtomicsCondition::kAllWaiters));
for (uint32_t i = 0; i < kThreads; i++) {
sema_execute_complete.ParkedWait(local_isolate);
}
ParkedScope parked(local_isolate);
for (auto& thread : threads) {
thread->ParkedJoin(parked);
}
EXPECT_EQ(0U, waiting_threads_count);
EXPECT_FALSE(mutex->IsHeld());
}
} // namespace internal
} // namespace v8
#endif // V8_CAN_CREATE_SHARED_HEAP