| // 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 "src/api/api.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.h" |
| #include "src/objects/contexts.h" |
| #include "test/cctest/cctest.h" |
| #include "test/cctest/heap/heap-utils.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| std::atomic<int> g_initialized_entries; |
| |
| class ScriptContextTableAccessUsedThread final : public v8::base::Thread { |
| public: |
| ScriptContextTableAccessUsedThread( |
| Isolate* isolate, Heap* heap, base::Semaphore* sema_started, |
| std::unique_ptr<PersistentHandles> ph, |
| Handle<ScriptContextTable> script_context_table) |
| : v8::base::Thread( |
| base::Thread::Options("ScriptContextTableAccessUsedThread")), |
| heap_(heap), |
| sema_started_(sema_started), |
| ph_(std::move(ph)), |
| script_context_table_(script_context_table) {} |
| |
| void Run() override { |
| LocalHeap local_heap(heap_, ThreadKind::kBackground, std::move(ph_)); |
| UnparkedScope unparked_scope(&local_heap); |
| LocalHandleScope scope(&local_heap); |
| |
| sema_started_->Signal(); |
| |
| for (int i = 0; i < script_context_table_->synchronized_used(); ++i) { |
| Context context = script_context_table_->get_context(i); |
| CHECK(context.IsScriptContext()); |
| } |
| } |
| |
| private: |
| Heap* heap_; |
| base::Semaphore* sema_started_; |
| std::unique_ptr<PersistentHandles> ph_; |
| Handle<ScriptContextTable> script_context_table_; |
| }; |
| |
| class AccessScriptContextTableThread final : public v8::base::Thread { |
| public: |
| AccessScriptContextTableThread(Isolate* isolate, Heap* heap, |
| base::Semaphore* sema_started, |
| std::unique_ptr<PersistentHandles> ph, |
| Handle<NativeContext> native_context) |
| : v8::base::Thread( |
| base::Thread::Options("AccessScriptContextTableThread")), |
| heap_(heap), |
| sema_started_(sema_started), |
| ph_(std::move(ph)), |
| native_context_(native_context) {} |
| |
| void Run() override { |
| LocalHeap local_heap(heap_, ThreadKind::kBackground, std::move(ph_)); |
| UnparkedScope unparked_scope(&local_heap); |
| LocalHandleScope scope(&local_heap); |
| |
| sema_started_->Signal(); |
| |
| for (int i = 0; i < 1000; ++i) { |
| // Read upper bound with relaxed semantics to not add any ordering |
| // constraints. |
| while (i >= g_initialized_entries.load(std::memory_order_relaxed)) { |
| } |
| auto script_context_table = Handle<ScriptContextTable>( |
| native_context_->synchronized_script_context_table(), &local_heap); |
| Handle<Context> context(script_context_table->get_context(i), |
| &local_heap); |
| CHECK(!context.is_null()); |
| } |
| } |
| |
| private: |
| Heap* heap_; |
| base::Semaphore* sema_started_; |
| std::unique_ptr<PersistentHandles> ph_; |
| Handle<NativeContext> native_context_; |
| }; |
| |
| TEST(ScriptContextTable_Extend) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| |
| Factory* factory = isolate->factory(); |
| Handle<NativeContext> native_context = factory->NewNativeContext(); |
| Handle<Map> script_context_map = |
| factory->NewMap(SCRIPT_CONTEXT_TYPE, kVariableSizeSentinel); |
| script_context_map->set_native_context(*native_context); |
| native_context->set_script_context_map(*script_context_map); |
| |
| Handle<ScriptContextTable> script_context_table = |
| factory->NewScriptContextTable(); |
| |
| Handle<ScopeInfo> scope_info = |
| ReadOnlyRoots(isolate).global_this_binding_scope_info_handle(); |
| |
| for (int i = 0; i < 10; ++i) { |
| Handle<Context> script_context = |
| factory->NewScriptContext(native_context, scope_info); |
| |
| script_context_table = |
| ScriptContextTable::Extend(script_context_table, script_context); |
| } |
| |
| std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles(); |
| Handle<ScriptContextTable> persistent_script_context_table = |
| ph->NewHandle(script_context_table); |
| |
| base::Semaphore sema_started(0); |
| |
| auto thread = std::make_unique<ScriptContextTableAccessUsedThread>( |
| isolate, isolate->heap(), &sema_started, std::move(ph), |
| persistent_script_context_table); |
| |
| CHECK(thread->Start()); |
| |
| sema_started.Wait(); |
| |
| for (int i = 0; i < 100; ++i) { |
| Handle<Context> context = |
| factory->NewScriptContext(native_context, scope_info); |
| script_context_table = |
| ScriptContextTable::Extend(script_context_table, context); |
| } |
| |
| thread->Join(); |
| } |
| |
| TEST(ScriptContextTable_AccessScriptContextTable) { |
| CcTest::InitializeVM(); |
| v8::HandleScope scope(CcTest::isolate()); |
| Isolate* isolate = CcTest::i_isolate(); |
| |
| Factory* factory = isolate->factory(); |
| Handle<NativeContext> native_context = factory->NewNativeContext(); |
| Handle<Map> script_context_map = |
| factory->NewMap(SCRIPT_CONTEXT_TYPE, kVariableSizeSentinel); |
| script_context_map->set_native_context(*native_context); |
| native_context->set_script_context_map(*script_context_map); |
| |
| Handle<ScopeInfo> scope_info = |
| ReadOnlyRoots(isolate).global_this_binding_scope_info_handle(); |
| |
| Handle<ScriptContextTable> script_context_table = |
| factory->NewScriptContextTable(); |
| Handle<Context> context = |
| factory->NewScriptContext(native_context, scope_info); |
| script_context_table = |
| ScriptContextTable::Extend(script_context_table, context); |
| int initialized_entries = 1; |
| g_initialized_entries.store(initialized_entries, std::memory_order_release); |
| |
| native_context->set_script_context_table(*script_context_table); |
| std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles(); |
| Handle<NativeContext> persistent_native_context = |
| ph->NewHandle(native_context); |
| |
| base::Semaphore sema_started(0); |
| |
| auto thread = std::make_unique<AccessScriptContextTableThread>( |
| isolate, isolate->heap(), &sema_started, std::move(ph), |
| persistent_native_context); |
| |
| CHECK(thread->Start()); |
| |
| sema_started.Wait(); |
| |
| for (; initialized_entries < 1000; ++initialized_entries) { |
| Handle<Context> context = |
| factory->NewScriptContext(native_context, scope_info); |
| script_context_table = |
| ScriptContextTable::Extend(script_context_table, context); |
| native_context->synchronized_set_script_context_table( |
| *script_context_table); |
| // Update with relaxed semantics to not introduce ordering constraints. |
| g_initialized_entries.store(initialized_entries, std::memory_order_relaxed); |
| } |
| g_initialized_entries.store(initialized_entries, std::memory_order_relaxed); |
| |
| thread->Join(); |
| } |
| |
| } // anonymous namespace |
| |
| } // namespace internal |
| } // namespace v8 |