1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gtest/gtest.h"
17 #include "runtime/include/coretypes/string.h"
18 #include "runtime/include/runtime.h"
19 #include "runtime/include/thread.h"
20 #include "runtime/include/gc_task.h"
21 #include "runtime/handle_base-inl.h"
22
23 #include <array>
24 #include <atomic>
25 #include <thread>
26 #include <mutex>
27 #include <condition_variable>
28
29 namespace panda::mem::test {
30
31 static constexpr uint32_t TEST_THREADS = 8;
32 static constexpr uint32_t TEST_ITERS = 100;
33
34 class MultithreadedInternStringTableTest : public testing::Test {
35 public:
MultithreadedInternStringTableTest()36 MultithreadedInternStringTableTest()
37 {
38 RuntimeOptions options;
39 options.SetShouldLoadBootPandaFiles(false);
40 options.SetShouldInitializeIntrinsics(false);
41
42 options.SetGcType("epsilon");
43 options.SetCompilerEnableJit(false);
44 Runtime::Create(options);
45 }
46
~MultithreadedInternStringTableTest()47 ~MultithreadedInternStringTableTest() override
48 {
49 Runtime::Destroy();
50 }
51
AllocUtf8String(std::vector<uint8_t> data)52 static coretypes::String *AllocUtf8String(std::vector<uint8_t> data)
53 {
54 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
55 return coretypes::String::CreateFromMUtf8(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx,
56 Runtime::GetCurrent()->GetPandaVM());
57 }
58
SetUp()59 void SetUp() override
60 {
61 table_ = new StringTable();
62 thread_ = panda::MTManagedThread::GetCurrent();
63 thread_->ManagedCodeBegin();
64 }
65
TearDown()66 void TearDown() override
67 {
68 thread_->ManagedCodeEnd();
69 delete table_;
70 table_ = nullptr;
71 }
72
GetTable() const73 StringTable *GetTable() const
74 {
75 return table_;
76 }
77
PreCheck()78 void PreCheck()
79 {
80 std::unique_lock<std::mutex> lk(pre_lock_);
81 counter_pre_++;
82 if (counter_pre_ == TEST_THREADS) {
83 pre_cv_.notify_all();
84 counter_pre_ = 0;
85 } else {
86 pre_cv_.wait(lk);
87 }
88 }
89
CheckSameString(coretypes::String * string)90 void CheckSameString(coretypes::String *string)
91 {
92 // Loop until lock is taken
93 while (lock_.test_and_set(std::memory_order_seq_cst)) {
94 };
95 if (string_ != nullptr) {
96 ASSERT_EQ(string_, string);
97 } else {
98 string_ = string;
99 }
100 lock_.clear(std::memory_order_seq_cst);
101 }
102
PostFree()103 void PostFree()
104 {
105 std::unique_lock<std::mutex> lk(post_lock_);
106 counter_post_++;
107 if (counter_post_ == TEST_THREADS) {
108 // There should be just one element in table
109 ASSERT_EQ(table_->Size(), 1);
110 string_ = nullptr;
111
112 {
113 os::memory::WriteLockHolder holder(table_->table_.table_lock_);
114 table_->table_.table_.clear();
115 }
116 {
117 os::memory::WriteLockHolder holder(table_->internal_table_.table_lock_);
118 table_->internal_table_.table_.clear();
119 }
120
121 post_cv_.notify_all();
122 counter_post_ = 0;
123 } else {
124 post_cv_.wait(lk);
125 }
126 }
127
128 protected:
129 panda::MTManagedThread *thread_ {nullptr};
130
131 std::mutex pre_lock_;
132 std::condition_variable pre_cv_;
133 int counter_pre_ = 0;
134 std::mutex post_lock_;
135 std::condition_variable post_cv_;
136 int counter_post_ = 0;
137 StringTable *table_ {nullptr};
138
139 std::atomic_flag lock_ {0};
140 coretypes::String *string_ {nullptr};
141 };
142
TestThreadEntry(MultithreadedInternStringTableTest * test)143 void TestThreadEntry(MultithreadedInternStringTableTest *test)
144 {
145 auto *this_thread =
146 panda::MTManagedThread::Create(panda::Runtime::GetCurrent(), panda::Runtime::GetCurrent()->GetPandaVM());
147 this_thread->ManagedCodeBegin();
148 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
149 std::vector<uint8_t> data {0xc2, 0xa7, 0x34, 0x00};
150 auto *table = test->GetTable();
151 for (uint32_t i = 0; i < TEST_ITERS; i++) {
152 test->PreCheck();
153 auto *interned_str = table->GetOrInternString(data.data(), 2U, ctx);
154 test->CheckSameString(interned_str);
155 test->PostFree();
156 }
157 this_thread->ManagedCodeEnd();
158 this_thread->Destroy();
159 }
160
TEST_F(MultithreadedInternStringTableTest,CheckInternReturnsSameString)161 TEST_F(MultithreadedInternStringTableTest, CheckInternReturnsSameString)
162 {
163 std::array<std::thread, TEST_THREADS> threads;
164 for (uint32_t i = 0; i < TEST_THREADS; i++) {
165 threads[i] = std::thread(TestThreadEntry, this);
166 }
167 for (uint32_t i = 0; i < TEST_THREADS; i++) {
168 threads[i].join();
169 }
170 }
171
172 } // namespace panda::mem::test
173