// Copyright 2021 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/optional.h" #include "src/base/platform/platform.h" #include "src/base/platform/semaphore.h" #include "src/heap/heap.h" #include "src/heap/parked-scope-inl.h" #include "src/objects/fixed-array.h" #include "test/unittests/heap/heap-utils.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 SharedHeapTest = TestJSSharedMemoryWithIsolate; class SharedHeapNoClientsTest : public TestJSSharedMemoryWithPlatform { public: SharedHeapNoClientsTest() { shared_space_isolate_wrapper.emplace(kNoCounters); shared_space_isolate_ = shared_space_isolate_wrapper->i_isolate(); } ~SharedHeapNoClientsTest() override { shared_space_isolate_ = nullptr; } v8::Isolate* shared_space_isolate() { return reinterpret_cast(i_shared_space_isolate()); } Isolate* i_shared_space_isolate() { return shared_space_isolate_; } private: Isolate* shared_space_isolate_; base::Optional shared_space_isolate_wrapper; }; namespace { const int kNumIterations = 2000; template void SetupClientIsolateAndRunCallback(Callback callback) { IsolateWrapper isolate_wrapper(kNoCounters); v8::Isolate* client_isolate = isolate_wrapper.isolate(); Isolate* i_client_isolate = reinterpret_cast(client_isolate); v8::Isolate::Scope isolate_scope(client_isolate); callback(client_isolate, i_client_isolate); } class SharedOldSpaceAllocationThread final : public ParkingThread { public: SharedOldSpaceAllocationThread() : ParkingThread(base::Thread::Options("SharedOldSpaceAllocationThread")) { } void Run() override { SetupClientIsolateAndRunCallback( [](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope scope(i_client_isolate); for (int i = 0; i < kNumIterations; i++) { i_client_isolate->factory()->NewFixedArray( 10, AllocationType::kSharedOld); } InvokeMajorGC(i_client_isolate); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } }; } // namespace TEST_F(SharedHeapTest, ConcurrentAllocationInSharedOldSpace) { i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector> threads; const int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } namespace { class SharedTrustedSpaceAllocationThread final : public ParkingThread { public: SharedTrustedSpaceAllocationThread() : ParkingThread( base::Thread::Options("SharedTrustedSpaceAllocationThread")) {} void Run() override { constexpr int kNumIterations = 2000; SetupClientIsolateAndRunCallback( [](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope scope(i_client_isolate); for (int i = 0; i < kNumIterations; i++) { i_client_isolate->factory()->NewTrustedByteArray( 10, AllocationType::kSharedTrusted); } InvokeMajorGC(i_client_isolate); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } }; } // namespace TEST_F(SharedHeapTest, ConcurrentAllocationInSharedTrustedSpace) { i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector> threads; const int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } namespace { class SharedLargeOldSpaceAllocationThread final : public ParkingThread { public: SharedLargeOldSpaceAllocationThread() : ParkingThread(base::Thread::Options("SharedOldSpaceAllocationThread")) { } void Run() override { SetupClientIsolateAndRunCallback( [](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope scope(i_client_isolate); const int kNumIterations = 50; for (int i = 0; i < kNumIterations; i++) { HandleScope scope(i_client_isolate); DirectHandle fixed_array = i_client_isolate->factory()->NewFixedArray( kMaxRegularHeapObjectSize / kTaggedSize, AllocationType::kSharedOld); CHECK(MemoryChunk::FromHeapObject(*fixed_array)->IsLargePage()); } InvokeMajorGC(i_client_isolate); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } }; } // namespace TEST_F(SharedHeapTest, ConcurrentAllocationInSharedLargeOldSpace) { i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector> threads; const int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } namespace { class SharedTrustedLargeObjectSpaceAllocationThread final : public ParkingThread { public: SharedTrustedLargeObjectSpaceAllocationThread() : ParkingThread(base::Thread::Options( "SharedTrustedLargeObjectSpaceAllocationThread")) {} void Run() override { SetupClientIsolateAndRunCallback( [](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope scope(i_client_isolate); constexpr int kNumIterations = 50; for (int i = 0; i < kNumIterations; i++) { HandleScope scope(i_client_isolate); DirectHandle fixed_array = i_client_isolate->factory()->NewTrustedByteArray( kMaxRegularHeapObjectSize, AllocationType::kSharedTrusted); CHECK(MemoryChunk::FromHeapObject(*fixed_array)->IsLargePage()); } InvokeMajorGC(i_client_isolate); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } }; } // namespace TEST_F(SharedHeapTest, ConcurrentAllocationInSharedTrustedLargeObjectSpace) { i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector< std::unique_ptr> threads; constexpr int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } TEST_F(SharedHeapTest, TrustedToSharedTrustedPointer) { Isolate* isolate = i_isolate(); Factory* factory = isolate->factory(); Handle constant_pool = factory->NewTrustedFixedArray(0); Handle handler_table = factory->NewTrustedByteArray(3, AllocationType::kSharedTrusted); CHECK_EQ(MemoryChunk::FromHeapObject(*handler_table) ->Metadata() ->owner() ->identity(), SHARED_TRUSTED_SPACE); // Use random bytes here since we don't ever run the bytecode. constexpr uint8_t kRawBytes[] = {0x1, 0x2, 0x3, 0x4}; constexpr int kRawBytesSize = sizeof(kRawBytes); constexpr int32_t kFrameSize = 32; constexpr uint16_t kParameterCount = 2; constexpr uint16_t kMaxArguments = 0; Handle bc = factory->NewBytecodeArray( kRawBytesSize, kRawBytes, kFrameSize, kParameterCount, kMaxArguments, constant_pool, handler_table); CHECK_EQ(MemoryChunk::FromHeapObject(*bc)->Metadata()->owner()->identity(), TRUSTED_SPACE); InvokeMajorGC(isolate); USE(bc); } namespace { class TrustedToSharedTrustedPointerOnClient final : public ParkingThread { public: explicit TrustedToSharedTrustedPointerOnClient(ParkingSemaphore* sem_ready, ParkingSemaphore* sema_done) : ParkingThread( base::Thread::Options("TrustedToSharedTrustedPointerOnClient")), sema_ready_(sem_ready), sema_done_(sema_done) {} void Run() override { SetupClientIsolateAndRunCallback([this](v8::Isolate* client_isolate, Isolate* i_client_isolate) { Factory* factory = i_client_isolate->factory(); HandleScope scope(i_client_isolate); Handle keep_alive_bc; { HandleScope nested_scope(i_client_isolate); Handle constant_pool = factory->NewTrustedFixedArray(0); Handle handler_table = factory->NewTrustedByteArray(3, AllocationType::kSharedTrusted); CHECK_EQ(MemoryChunk::FromHeapObject(*handler_table) ->Metadata() ->owner() ->identity(), SHARED_TRUSTED_SPACE); // Use random bytes here since we don't ever run the bytecode. constexpr uint8_t kRawBytes[] = {0x1, 0x2, 0x3, 0x4}; constexpr int kRawBytesSize = sizeof(kRawBytes); constexpr int32_t kFrameSize = 32; constexpr uint16_t kParameterCount = 2; constexpr uint16_t kMaxArguments = 0; Handle bc = factory->NewBytecodeArray( kRawBytesSize, kRawBytes, kFrameSize, kParameterCount, kMaxArguments, constant_pool, handler_table); keep_alive_bc = nested_scope.CloseAndEscape(bc); } sema_ready_->Signal(); sema_done_->ParkedWait(i_client_isolate->main_thread_local_isolate()); Tagged handler_table = keep_alive_bc->handler_table(); CHECK(IsTrustedByteArray(handler_table)); CHECK_EQ(handler_table->length(), 3); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } private: ParkingSemaphore* sema_ready_; ParkingSemaphore* sema_done_; }; } // namespace TEST_F(SharedHeapTest, TrustedToSharedTrustedPointerOnClient) { std::vector> threads; const int kThreads = 4; ParkingSemaphore sema_ready(0); ParkingSemaphore sema_done(0); for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique( &sema_ready, &sema_done); CHECK(thread->Start()); threads.push_back(std::move(thread)); } LocalIsolate* local_isolate = i_isolate()->main_thread_local_isolate(); for (int i = 0; i < kThreads; i++) { sema_ready.ParkedWait(local_isolate); } InvokeMajorGC(i_isolate()); for (int i = 0; i < kThreads; i++) { sema_done.Signal(); } ParkingThread::ParkedJoinAll(local_isolate, threads); } namespace { class SharedMapSpaceAllocationThread final : public ParkingThread { public: SharedMapSpaceAllocationThread() : ParkingThread(base::Thread::Options("SharedMapSpaceAllocationThread")) { } void Run() override { SetupClientIsolateAndRunCallback( [](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope scope(i_client_isolate); for (int i = 0; i < kNumIterations; i++) { i_client_isolate->factory()->NewContextlessMap( NATIVE_CONTEXT_TYPE, kVariableSizeSentinel, TERMINAL_FAST_ELEMENTS_KIND, 0, AllocationType::kSharedMap); } InvokeMajorGC(i_client_isolate); v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), client_isolate); }); } }; } // namespace TEST_F(SharedHeapTest, ConcurrentAllocationInSharedMapSpace) { i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector> threads; const int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } TEST_F(SharedHeapNoClientsTest, SharedCollectionWithoutClients) { ::v8::internal::InvokeMajorGC(i_shared_space_isolate()); } void AllocateInSharedHeap(int iterations = 100) { SetupClientIsolateAndRunCallback([iterations](v8::Isolate* client_isolate, Isolate* i_client_isolate) { HandleScope outer_scope(i_client_isolate); std::vector> arrays_in_handles; const int kKeptAliveInHandle = 1000; const int kKeptAliveInHeap = 100; DirectHandle arrays_in_heap = i_client_isolate->factory()->NewFixedArray(kKeptAliveInHeap, AllocationType::kYoung); for (int i = 0; i < kNumIterations * iterations; i++) { HandleScope scope(i_client_isolate); Handle array = i_client_isolate->factory()->NewFixedArray( 100, AllocationType::kSharedOld); if (i < kKeptAliveInHandle) { // Keep some of those arrays alive across GCs through handles. arrays_in_handles.push_back(scope.CloseAndEscape(array)); } if (i < kKeptAliveInHeap) { // Keep some of those arrays alive across GCs through client heap // references. arrays_in_heap->set(i, *array); } i_client_isolate->factory()->NewFixedArray(100, AllocationType::kYoung); } for (DirectHandle array : arrays_in_handles) { CHECK_EQ(array->length(), 100); } for (int i = 0; i < kKeptAliveInHeap; i++) { Tagged array = Cast(arrays_in_heap->get(i)); CHECK_EQ(array->length(), 100); } }); } TEST_F(SharedHeapTest, SharedCollectionWithOneClient) { v8_flags.max_old_space_size = 8; i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( []() { AllocateInSharedHeap(); }); } namespace { class SharedFixedArrayAllocationThread final : public ParkingThread { public: SharedFixedArrayAllocationThread() : ParkingThread( base::Thread::Options("SharedFixedArrayAllocationThread")) {} void Run() override { AllocateInSharedHeap(5); } }; } // namespace TEST_F(SharedHeapTest, SharedCollectionWithMultipleClients) { v8_flags.max_old_space_size = 8; i_isolate()->main_thread_local_isolate()->ExecuteMainThreadWhileParked( [](const ParkedScope& parked) { std::vector> threads; const int kThreads = 4; for (int i = 0; i < kThreads; i++) { auto thread = std::make_unique(); CHECK(thread->Start()); threads.push_back(std::move(thread)); } ParkingThread::ParkedJoinAll(parked, threads); }); } namespace { /** * The following two classes implement a recurring pattern for testing the * shared heap: two isolates (main and client), used respectively by the main * thread and a concurrent thread, that execute arbitrary fragments of code * (shown below in angular brackets) and synchronize in the following way * using parked semaphores: * * main thread concurrent thread * --------------------------------------------------------------- * * | * start thread ----------\ * | \---------> * | | * | /-------- signal ready * wait for ready <--------/ | * | * signal execute ---------\ | * | \-----> wait for execute * | * | /------ signal complete * wait for complete <------/ | * | | * * | /----------- exit * join thread <----------/ * * * Both threads allocate an arbitrary state object on their stack, which * may contain information that is shared between the executed fragments * of code. */ template class ConcurrentThread final : public ParkingThread { public: using ThreadType = ConcurrentThread; using Callback = void(ThreadType*); explicit ConcurrentThread( bool wait_while_parked, v8::base::Semaphore* sema_ready = nullptr, v8::base::Semaphore* sema_execute_start = nullptr, v8::base::Semaphore* sema_execute_complete = nullptr) : ParkingThread(Options("ConcurrentThread")), sema_ready_(sema_ready), sema_execute_start_(sema_execute_start), sema_execute_complete_(sema_execute_complete), wait_while_parked_(wait_while_parked) {} void Run() override { IsolateWrapper isolate_wrapper(kNoCounters); i_client_isolate_ = isolate_wrapper.i_isolate(); // Allocate the state on the stack, so that handles, direct handles or raw // pointers are stack-allocated. State state; state_ = &state; if (setup_callback_) setup_callback_(this); if (sema_ready_) sema_ready_->Signal(); if (sema_execute_start_) { if (wait_while_parked_) { // Park and wait. i_client_isolate_->main_thread_local_isolate() ->ExecuteMainThreadWhileParked( [this]() { sema_execute_start_->Wait(); }); } else { // Do not park, but enter a safepoint every now and then. const auto timeout = base::TimeDelta::FromMilliseconds(100); do { i_client_isolate_->main_thread_local_isolate()->heap()->Safepoint(); } while (!sema_execute_start_->WaitFor(timeout)); } } if (execute_callback_) execute_callback_(this); if (sema_execute_complete_) sema_execute_complete_->Signal(); if (complete_callback_) complete_callback_(this); i_client_isolate_ = nullptr; state_ = nullptr; } Isolate* i_client_isolate() const { DCHECK_NOT_NULL(i_client_isolate_); return i_client_isolate_; } v8::Isolate* client_isolate() const { return reinterpret_cast(i_client_isolate_); } State* state() { DCHECK_NOT_NULL(state_); return state_; } void with_setup(Callback* callback) { setup_callback_ = callback; } void with_execute(Callback* callback) { execute_callback_ = callback; } void with_complete(Callback* callback) { complete_callback_ = callback; } private: Isolate* i_client_isolate_ = nullptr; State* state_ = nullptr; v8::base::Semaphore* sema_ready_ = nullptr; v8::base::Semaphore* sema_execute_start_ = nullptr; v8::base::Semaphore* sema_execute_complete_ = nullptr; Callback* setup_callback_ = nullptr; Callback* execute_callback_ = nullptr; Callback* complete_callback_ = nullptr; bool wait_while_parked_; }; template class SharedHeapTestBase : public TestJSSharedMemoryWithNativeContext { public: using TestType = SharedHeapTestBase; using Callback = void(TestType*); using ThreadType = ConcurrentThread; using ThreadCallback = typename ThreadType::Callback; SharedHeapTestBase() : thread_(std::make_unique(wait_while_parked, &sema_ready_, &sema_execute_start_, &sema_execute_complete_)) {} void Interact() { // Allocate the state on the stack, so that handles, direct handles or raw // pointers are stack-allocated. State state; state_ = &state; if (setup_callback_) setup_callback_(this); CHECK(thread()->Start()); sema_ready_.Wait(); if (execute_callback_) execute_callback_(this); sema_execute_start_.Signal(); sema_execute_complete_.Wait(); if (complete_callback_) complete_callback_(this); thread()->ParkedJoin(i_isolate()->main_thread_local_isolate()); if (teardown_callback_) teardown_callback_(this); } ConcurrentThread* thread() const { DCHECK(thread_); return thread_.get(); } State* state() { DCHECK_NOT_NULL(state_); return state_; } void with_setup(Callback* callback) { setup_callback_ = callback; } void with_execute(Callback* callback) { execute_callback_ = callback; } void with_complete(Callback* callback) { complete_callback_ = callback; } void with_teardown(Callback* callback) { teardown_callback_ = callback; } private: State* state_ = nullptr; std::unique_ptr> thread_; v8::base::Semaphore sema_ready_{0}; v8::base::Semaphore sema_execute_start_{0}; v8::base::Semaphore sema_execute_complete_{0}; Callback* setup_callback_ = nullptr; Callback* execute_callback_ = nullptr; Callback* complete_callback_ = nullptr; Callback* teardown_callback_ = nullptr; }; } // namespace #define TEST_SCENARIO(test_class, test_method, test_name, allocation, space) \ TEST_F(test_class, test_name) { \ test_method(this); \ } #define TEST_ALL_SCENARIA(test_class, test_prefix, test_method) \ TEST_SCENARIO(test_class, test_method, test_prefix##YoungYoung, \ AllocationType::kYoung, NEW_SPACE) \ TEST_SCENARIO(test_class, test_method, test_prefix##YoungOld, \ AllocationType::kYoung, OLD_SPACE) \ TEST_SCENARIO(test_class, test_method, test_prefix##OldYoung, \ AllocationType::kOld, NEW_SPACE) \ TEST_SCENARIO(test_class, test_method, test_prefix##OldOld, \ AllocationType::kOld, OLD_SPACE) \ TEST_SCENARIO(test_class, test_method, test_prefix##SharedYoung, \ AllocationType::kSharedOld, NEW_SPACE) \ TEST_SCENARIO(test_class, test_method, test_prefix##SharedOld, \ AllocationType::kSharedOld, OLD_SPACE) namespace { // Testing the shared heap using ordinary (indirect) handles. struct StateWithHandle { base::Optional scope; Handle handle; Global weak; }; template void AllocateWithHandle(Isolate* isolate, StateWithHandle* state) { // Install a handle scope. state->scope.emplace(isolate); // Allocate a fixed array, keep a handle and a weak reference. state->handle = isolate->factory()->NewFixedArray(size, allocation); Local l = Utils::FixedArrayToLocal(state->handle); state->weak.Reset(reinterpret_cast(isolate), l); state->weak.SetWeak(); } using SharedHeapTestStateWithHandleParked = SharedHeapTestBase; using SharedHeapTestStateWithHandleUnparked = SharedHeapTestBase; void InvokeGC(AllocationSpace space, Isolate* isolate) { space == NEW_SPACE ? InvokeMinorGC(isolate) : InvokeMajorGC(isolate); } template void ToEachTheirOwnWithHandle(TestType* test) { using ThreadType = typename TestType::ThreadType; ThreadType* thread = test->thread(); // Install all the callbacks. test->with_setup([](TestType* test) { AllocateWithHandle(test->i_isolate(), test->state()); }); thread->with_setup([](ThreadType* thread) { AllocateWithHandle(thread->i_client_isolate(), thread->state()); }); test->with_execute( [](TestType* test) { InvokeGC(space, test->i_isolate()); }); thread->with_execute( [](ThreadType* thread) { InvokeGC(space, thread->i_client_isolate()); }); test->with_complete([](TestType* test) { // The handle should keep the fixed array from being reclaimed. EXPECT_FALSE(test->state()->weak.IsEmpty()); }); thread->with_complete([](ThreadType* thread) { // The handle should keep the fixed array from being reclaimed. EXPECT_FALSE(thread->state()->weak.IsEmpty()); thread->state()->scope.reset(); // Deallocate the handle scope. InvokeGC(space, thread->i_client_isolate()); }); test->with_teardown([](TestType* test) { test->state()->scope.reset(); // Deallocate the handle scope. InvokeGC(space, test->i_isolate()); }); // Perform the test. test->Interact(); } } // namespace TEST_ALL_SCENARIA(SharedHeapTestStateWithHandleParked, ToEachTheirOwn, ToEachTheirOwnWithHandle) TEST_ALL_SCENARIA(SharedHeapTestStateWithHandleUnparked, ToEachTheirOwn, ToEachTheirOwnWithHandle) #ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING namespace { // Testing the shared heap using raw pointers. // This works only with conservative stack scanning. struct StateWithRawPointer { Address ptr; Global weak; }; template void AllocateWithRawPointer(Isolate* isolate, StateWithRawPointer* state) { // Allocate a fixed array, keep a raw pointer and a weak reference. HandleScope scope(isolate); DirectHandle h = isolate->factory()->NewFixedArray(size, allocation); state->ptr = (*h).ptr(); Local l = Utils::FixedArrayToLocal(h, isolate); state->weak.Reset(reinterpret_cast(isolate), l); state->weak.SetWeak(); } using SharedHeapTestStateWithRawPointerParked = SharedHeapTestBase; using SharedHeapTestStateWithRawPointerUnparked = SharedHeapTestBase; template void ToEachTheirOwnWithRawPointer(TestType* test) { using ThreadType = typename TestType::ThreadType; ThreadType* thread = test->thread(); // Install all the callbacks. test->with_setup([](TestType* test) { AllocateWithRawPointer(test->i_isolate(), test->state()); }); thread->with_setup([](ThreadType* thread) { AllocateWithRawPointer(thread->i_client_isolate(), thread->state()); }); test->with_execute( [](TestType* test) { InvokeGC(space, test->i_isolate()); }); thread->with_execute( [](ThreadType* thread) { InvokeGC(space, thread->i_client_isolate()); }); test->with_complete([](TestType* test) { // With conservative stack scanning, the raw pointer should keep the fixed // array from being reclaimed. EXPECT_FALSE(test->state()->weak.IsEmpty()); }); thread->with_complete([](ThreadType* thread) { // With conservative stack scanning, the raw pointer should keep the fixed // array from being reclaimed. EXPECT_FALSE(thread->state()->weak.IsEmpty()); InvokeGC(space, thread->i_client_isolate()); }); test->with_teardown( [](TestType* test) { InvokeGC(space, test->i_isolate()); }); // Perform the test. test->Interact(); } } // namespace TEST_ALL_SCENARIA(SharedHeapTestStateWithRawPointerParked, ToEachTheirOwn, ToEachTheirOwnWithRawPointer) TEST_ALL_SCENARIA(SharedHeapTestStateWithRawPointerUnparked, ToEachTheirOwn, ToEachTheirOwnWithRawPointer) #endif // V8_ENABLE_CONSERVATIVE_STACK_SCANNING #undef TEST_SCENARIO #undef TEST_ALL_SCENARIA } // namespace internal } // namespace v8 #endif // V8_CAN_CREATE_SHARED_HEAP_BOOL