// Copyright 2020 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 #include "src/api/api.h" #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" #include "src/base/platform/semaphore.h" #include "src/handles/handles-inl.h" #include "src/handles/local-handles-inl.h" #include "src/handles/persistent-handles.h" #include "src/heap/heap.h" #include "src/heap/local-heap-inl.h" #include "src/heap/local-heap.h" #include "src/heap/safepoint.h" #include "src/objects/heap-number.h" #include "test/cctest/cctest.h" #include "test/cctest/heap/heap-utils.h" namespace v8 { namespace internal { static constexpr int kNumHandles = kHandleBlockSize * 2 + kHandleBlockSize / 2; namespace { class PersistentHandlesThread final : public v8::base::Thread { public: PersistentHandlesThread(Heap* heap, std::vector> handles, std::unique_ptr ph, HeapNumber number, base::Semaphore* sema_started, base::Semaphore* sema_gc_finished) : v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")), heap_(heap), handles_(std::move(handles)), ph_(std::move(ph)), number_(number), sema_started_(sema_started), sema_gc_finished_(sema_gc_finished) {} void Run() override { LocalHeap local_heap(heap_, std::move(ph_)); LocalHandleScope scope(&local_heap); for (int i = 0; i < kNumHandles; i++) { handles_.push_back(local_heap.NewPersistentHandle(number_)); } sema_started_->Signal(); { ParkedScope scope(&local_heap); sema_gc_finished_->Wait(); } for (Handle handle : handles_) { CHECK_EQ(42.0, handle->value()); } CHECK_EQ(handles_.size(), kNumHandles * 2); CHECK(!ph_); ph_ = local_heap.DetachPersistentHandles(); } std::unique_ptr DetachPersistentHandles() { CHECK(ph_); return std::move(ph_); } private: Heap* heap_; std::vector> handles_; std::unique_ptr ph_; HeapNumber number_; base::Semaphore* sema_started_; base::Semaphore* sema_gc_finished_; }; TEST(CreatePersistentHandles) { heap::EnsureFlagLocalHeapsEnabled(); CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); std::unique_ptr ph = isolate->NewPersistentHandles(); std::vector> handles; HandleScope handle_scope(isolate); Handle number = isolate->factory()->NewHeapNumber(42.0); for (int i = 0; i < kNumHandles; i++) { handles.push_back(ph->NewHandle(number)); } base::Semaphore sema_started(0); base::Semaphore sema_gc_finished(0); // pass persistent handles to background thread std::unique_ptr thread(new PersistentHandlesThread( isolate->heap(), std::move(handles), std::move(ph), *number, &sema_started, &sema_gc_finished)); CHECK(thread->Start()); sema_started.Wait(); CcTest::CollectAllGarbage(); sema_gc_finished.Signal(); thread->Join(); // get persistent handles back to main thread ph = thread->DetachPersistentHandles(); ph->NewHandle(number); } TEST(DereferencePersistentHandle) { heap::EnsureFlagLocalHeapsEnabled(); CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); std::unique_ptr phs = isolate->NewPersistentHandles(); Handle ph; { HandleScope handle_scope(isolate); Handle number = isolate->factory()->NewHeapNumber(42.0); ph = phs->NewHandle(number); } { LocalHeap local_heap(isolate->heap(), std::move(phs)); CHECK_EQ(42, ph->value()); DisallowHandleDereference disallow_scope; CHECK_EQ(42, ph->value()); } } TEST(NewPersistentHandleFailsWhenParked) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); LocalHeap local_heap(isolate->heap()); ParkedScope scope(&local_heap); // Fail here in debug mode: Persistent handles can't be created if local heap // is parked local_heap.NewPersistentHandle(Smi::FromInt(1)); } TEST(NewPersistentHandleFailsWhenParkedExplicit) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); LocalHeap local_heap(isolate->heap(), isolate->NewPersistentHandles()); ParkedScope scope(&local_heap); // Fail here in debug mode: Persistent handles can't be created if local heap // is parked local_heap.NewPersistentHandle(Smi::FromInt(1)); } } // anonymous namespace } // namespace internal } // namespace v8