test-concurrent-descriptor-array.cc 6.46 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// 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"
11
#include "src/heap/local-heap-inl.h"
12
#include "src/heap/local-heap.h"
13
#include "src/heap/parked-scope.h"
14 15 16 17 18 19 20 21 22 23
#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 {

24
class ConcurrentSearchThread final : public v8::base::Thread {
25
 public:
26 27 28
  ConcurrentSearchThread(Heap* heap, std::vector<Handle<JSObject>> handles,
                         std::unique_ptr<PersistentHandles> ph,
                         Handle<Name> name, base::Semaphore* sema_started)
29 30 31 32 33 34 35 36
      : v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")),
        heap_(heap),
        handles_(std::move(handles)),
        ph_(std::move(ph)),
        name_(name),
        sema_started_(sema_started) {}

  void Run() override {
37
    LocalHeap local_heap(heap_, ThreadKind::kBackground, std::move(ph_));
38
    UnparkedScope unparked_scope(&local_heap);
39 40 41
    LocalHandleScope scope(&local_heap);

    for (int i = 0; i < kNumHandles; i++) {
42
      handles_.push_back(local_heap.NewPersistentHandle(handles_[0]));
43 44 45 46 47 48 49 50 51 52
    }

    sema_started_->Signal();

    for (Handle<JSObject> handle : handles_) {
      // Lookup the named property on the {map}.
      CHECK(name_->IsUniqueName());
      Handle<Map> map(handle->map(), &local_heap);

      Handle<DescriptorArray> descriptors(
53
          map->instance_descriptors(kAcquireLoad), &local_heap);
54 55 56 57 58 59 60 61 62
      bool is_background_thread = true;
      InternalIndex const number =
          descriptors->Search(*name_, *map, is_background_thread);
      CHECK(number.is_found());
    }

    CHECK_EQ(handles_.size(), kNumHandles * 2);
  }

63
 private:
64 65 66 67 68 69 70 71 72
  Heap* heap_;
  std::vector<Handle<JSObject>> handles_;
  std::unique_ptr<PersistentHandles> ph_;
  Handle<Name> name_;
  base::Semaphore* sema_started_;
};

// Uses linear search on a flat object, with up to 8 elements.
TEST(LinearSearchFlatObject) {
73
  CcTest::InitializeVM();
74 75 76 77 78 79 80 81 82
  Isolate* isolate = CcTest::i_isolate();

  std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
  std::vector<Handle<JSObject>> handles;

  auto factory = isolate->factory();
  HandleScope handle_scope(isolate);

  Handle<JSFunction> function =
83
      factory->NewFunctionForTesting(factory->empty_string());
84 85 86 87 88 89 90 91 92 93
  Handle<JSObject> js_object = factory->NewJSObject(function);
  Handle<String> name = CcTest::MakeString("property");
  Handle<Object> value = CcTest::MakeString("dummy_value");
  // For the default constructor function no in-object properties are reserved
  // hence adding a single property will initialize the property-array.
  JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, name, value,
                                                    NONE)
      .Check();

  for (int i = 0; i < kNumHandles; i++) {
94
    handles.push_back(ph->NewHandle(js_object));
95 96
  }

97
  Handle<Name> persistent_name = ph->NewHandle(name);
98 99 100 101

  base::Semaphore sema_started(0);

  // Pass persistent handles to background thread.
102
  std::unique_ptr<ConcurrentSearchThread> thread(new ConcurrentSearchThread(
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
      isolate->heap(), std::move(handles), std::move(ph), persistent_name,
      &sema_started));
  CHECK(thread->Start());

  sema_started.Wait();

  // Exercise descriptor in main thread too.
  for (int i = 0; i < 7; ++i) {
    Handle<String> filler_name = CcTest::MakeName("filler_property_", i);
    Handle<Object> filler_value = CcTest::MakeString("dummy_value");
    JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, filler_name,
                                                      filler_value, NONE)
        .Check();
  }
  CHECK_EQ(js_object->map().NumberOfOwnDescriptors(), 8);

  thread->Join();
}

122 123 124 125 126 127 128 129 130 131 132 133
// Uses linear search on a flat object, which has more than 8 elements.
TEST(LinearSearchFlatObject_ManyElements) {
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();

  std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
  std::vector<Handle<JSObject>> handles;

  auto factory = isolate->factory();
  HandleScope handle_scope(isolate);

  Handle<JSFunction> function =
134
      factory->NewFunctionForTesting(factory->empty_string());
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
  Handle<JSObject> js_object = factory->NewJSObject(function);
  Handle<String> name = CcTest::MakeString("property");
  Handle<Object> value = CcTest::MakeString("dummy_value");
  // For the default constructor function no in-object properties are reserved
  // hence adding a single property will initialize the property-array.
  JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, name, value,
                                                    NONE)
      .Check();

  // If we have more than 8 properties we would do a binary search. However,
  // since we are going search in a background thread, we force a linear search
  // that is safe to do in the background.
  for (int i = 0; i < 10; ++i) {
    Handle<String> filler_name = CcTest::MakeName("filler_property_", i);
    Handle<Object> filler_value = CcTest::MakeString("dummy_value");
    JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, filler_name,
                                                      filler_value, NONE)
        .Check();
  }
  CHECK_GT(js_object->map().NumberOfOwnDescriptors(), 8);

  for (int i = 0; i < kNumHandles; i++) {
157
    handles.push_back(ph->NewHandle(js_object));
158 159
  }

160
  Handle<Name> persistent_name = ph->NewHandle(name);
161 162 163 164

  base::Semaphore sema_started(0);

  // Pass persistent handles to background thread.
165
  std::unique_ptr<ConcurrentSearchThread> thread(new ConcurrentSearchThread(
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
      isolate->heap(), std::move(handles), std::move(ph), persistent_name,
      &sema_started));
  CHECK(thread->Start());

  sema_started.Wait();

  // Exercise descriptor in main thread too.
  for (int i = 10; i < 20; ++i) {
    Handle<String> filler_name = CcTest::MakeName("filler_property_", i);
    Handle<Object> filler_value = CcTest::MakeString("dummy_value");
    JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, filler_name,
                                                      filler_value, NONE)
        .Check();
  }

  thread->Join();
}

184 185 186 187
}  // anonymous namespace

}  // namespace internal
}  // namespace v8