text: // 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/heap/parked-scope.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_->used(kAcquireLoad); ++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
  