• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 #include <random>
29 #include <climits>
30 #include <functional>
31 
32 namespace panda::mem::test {
33 
34 static constexpr uint32_t TEST_THREADS = 8;
35 static constexpr uint32_t TEST_ITERS = 1000;
36 static constexpr uint32_t TEST_ARRAY_SIZE = TEST_THREADS * 1000;
37 
38 class MultithreadedInternStringTableTest : public testing::Test {
39 public:
MultithreadedInternStringTableTest()40     MultithreadedInternStringTableTest()
41     {
42         RuntimeOptions options;
43         options.SetShouldLoadBootPandaFiles(false);
44         options.SetShouldInitializeIntrinsics(false);
45 
46         options.SetGcType("epsilon");
47         options.SetCompilerEnableJit(false);
48         Runtime::Create(options);
49     }
50 
~MultithreadedInternStringTableTest()51     ~MultithreadedInternStringTableTest() override
52     {
53         Runtime::Destroy();
54     }
55 
AllocUtf8String(std::vector<uint8_t> data)56     static coretypes::String *AllocUtf8String(std::vector<uint8_t> data)
57     {
58         LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
59         return coretypes::String::CreateFromMUtf8(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx,
60                                                   Runtime::GetCurrent()->GetPandaVM());
61     }
62 
SetUp()63     void SetUp() override
64     {
65         table_ = new StringTable();
66         thread_ = panda::MTManagedThread::GetCurrent();
67         thread_->ManagedCodeBegin();
68     }
69 
TearDown()70     void TearDown() override
71     {
72         thread_->ManagedCodeEnd();
73         delete table_;
74         table_ = nullptr;
75     }
76 
GetTable()77     StringTable *GetTable()
78     {
79         return table_;
80     }
81 
PreCheck()82     void PreCheck()
83     {
84         std::unique_lock<std::mutex> lk(pre_lock_);
85         counter_pre_++;
86         if (counter_pre_ == TEST_THREADS) {
87             pre_cv_.notify_all();
88             counter_pre_ = 0;
89         } else {
90             pre_cv_.wait(lk);
91         }
92     }
93 
CheckSameString(coretypes::String * string)94     void CheckSameString(coretypes::String *string)
95     {
96         // Loop until lock is taken
97         while (lock_.test_and_set(std::memory_order_seq_cst)) {
98         }
99         if (string_ != nullptr) {
100             ASSERT_EQ(string_, string);
101         } else {
102             string_ = string;
103         }
104         lock_.clear(std::memory_order_seq_cst);
105     }
106 
PostFree()107     void PostFree()
108     {
109         std::unique_lock<std::mutex> lk(post_lock_);
110         counter_post_++;
111         if (counter_post_ == TEST_THREADS) {
112             // There should be just one element in table
113             ASSERT_EQ(table_->Size(), 1);
114             string_ = nullptr;
115 
116             {
117                 os::memory::WriteLockHolder holder(table_->table_.table_lock_);
118                 table_->table_.table_.clear();
119             }
120             {
121                 os::memory::WriteLockHolder holder(table_->internal_table_.table_lock_);
122                 table_->internal_table_.table_.clear();
123             }
124 
125             post_cv_.notify_all();
126             counter_post_ = 0;
127         } else {
128             post_cv_.wait(lk);
129         }
130     }
131 
132     std::mutex mutex_;
133 
134 protected:
135     panda::MTManagedThread *thread_ {nullptr};
136 
137     std::mutex pre_lock_;
138     std::condition_variable pre_cv_;
139     int counter_pre_ = 0;
140     std::mutex post_lock_;
141     std::condition_variable post_cv_;
142     int counter_post_ = 0;
143     StringTable *table_ {nullptr};
144 
145     std::atomic_flag lock_ {0};
146     coretypes::String *string_ {nullptr};
147 };
148 
TestThreadEntry(MultithreadedInternStringTableTest * test)149 void TestThreadEntry(MultithreadedInternStringTableTest *test)
150 {
151     auto *this_thread =
152         panda::MTManagedThread::Create(panda::Runtime::GetCurrent(), panda::Runtime::GetCurrent()->GetPandaVM());
153     this_thread->ManagedCodeBegin();
154     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
155     std::vector<uint8_t> data {0xc2, 0xa7, 0x34, 0x00};
156     auto *table = test->GetTable();
157     for (uint32_t i = 0; i < TEST_ITERS; i++) {
158         test->PreCheck();
159         auto *interned_str = table->GetOrInternString(data.data(), 2, ctx);
160         test->CheckSameString(interned_str);
161         test->PostFree();
162     }
163     this_thread->ManagedCodeEnd();
164     this_thread->Destroy();
165 }
166 
TestConcurrentInsertion(const std::array<std::array<uint8_t,4>,TEST_ARRAY_SIZE> & strings,uint32_t & array_item,MultithreadedInternStringTableTest * test)167 void TestConcurrentInsertion(const std::array<std::array<uint8_t, 4>, TEST_ARRAY_SIZE> &strings, uint32_t &array_item,
168                              MultithreadedInternStringTableTest *test)
169 {
170     auto *this_thread =
171         panda::MTManagedThread::Create(panda::Runtime::GetCurrent(), panda::Runtime::GetCurrent()->GetPandaVM());
172     this_thread->ManagedCodeBegin();
173     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
174     auto *table = test->GetTable();
175 
176     uint32_t current_array_item = 0;
177     while (true) {
178         {
179             std::lock_guard<std::mutex> lock_guard(test->mutex_);
180             if (array_item >= TEST_ARRAY_SIZE) {
181                 break;
182             }
183             current_array_item = array_item++;
184         }
185         table->GetOrInternString(strings[current_array_item].data(), 2, ctx);
186     }
187 
188     this_thread->ManagedCodeEnd();
189     this_thread->Destroy();
190 }
191 
TEST_F(MultithreadedInternStringTableTest,ConcurrentInsertion)192 TEST_F(MultithreadedInternStringTableTest, ConcurrentInsertion)
193 {
194     std::array<std::thread, TEST_THREADS> threads;
195     std::array<std::array<uint8_t, 4>, TEST_ARRAY_SIZE> strings;
196     std::random_device random_device;
197     std::mt19937 engine {random_device()};
198     std::uniform_int_distribution<uint8_t> dist(0, 255);
199     uint32_t array_item = 0;
200 
201     for (uint32_t i = 0; i < TEST_ARRAY_SIZE; i++) {
202         strings[i] = {0xc2, dist(engine), dist(engine), 0x00};
203     }
204 
205     for (uint32_t i = 0; i < TEST_THREADS; i++) {
206         threads[i] = std::thread(TestConcurrentInsertion, std::ref(strings), std::ref(array_item), this);
207     }
208 
209     for (uint32_t i = 0; i < TEST_THREADS; i++) {
210         threads[i].join();
211     }
212 }
213 
TEST_F(MultithreadedInternStringTableTest,CheckInternReturnsSameString)214 TEST_F(MultithreadedInternStringTableTest, CheckInternReturnsSameString)
215 {
216     std::array<std::thread, TEST_THREADS> threads;
217     for (uint32_t i = 0; i < TEST_THREADS; i++) {
218         threads[i] = std::thread(TestThreadEntry, this);
219     }
220     for (uint32_t i = 0; i < TEST_THREADS; i++) {
221         threads[i].join();
222     }
223 }
224 }  // namespace panda::mem::test
225