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 <pthread.h>
17 
18 #include "gtest/gtest.h"
19 #include "os/mutex.h"
20 #include "os/thread.h"
21 
22 namespace panda::test {
23 
24 class MutexTest : public testing::Test {};
25 
26 constexpr size_t N = 10;
27 constexpr size_t WRITE_LOCK_SLEEP_TIME = 50;
28 constexpr size_t READ_LOCK_SLEEP_TIME = 1000;
29 
30 struct ThreadRWLockArgs {
31     os::memory::RWLock *rwlock;
32     size_t *index;
33 };
34 
35 struct ThreadMutexArgs {
36     os::memory::Mutex *lock;
37     size_t *index;
38 };
39 
TestThread1(void *input)40 static void *TestThread1(void *input)
41 {
42     ASSERT(input != nullptr);
43     ThreadRWLockArgs *arg = reinterpret_cast<ThreadRWLockArgs *>(input);
44     arg->rwlock->WriteLock();
45     (*(arg->index))++;
46     os::thread::NativeSleep(WRITE_LOCK_SLEEP_TIME);
47     arg->rwlock->Unlock();
48     return nullptr;
49 }
50 
TestThread2(void *input)51 static void *TestThread2(void *input)
52 {
53     ASSERT(input != nullptr);
54     ThreadRWLockArgs *arg = reinterpret_cast<ThreadRWLockArgs *>(input);
55     arg->rwlock->ReadLock();
56     os::thread::NativeSleep(READ_LOCK_SLEEP_TIME);
57     arg->rwlock->Unlock();
58     return nullptr;
59 }
60 
TestThread3(void *input)61 static void *TestThread3(void *input)
62 {
63     ASSERT(input != nullptr);
64     ThreadRWLockArgs *arg = reinterpret_cast<ThreadRWLockArgs *>(input);
65     bool res = arg->rwlock->TryReadLock();
66     if (res) {
67         (*(arg->index))++;
68         arg->rwlock->Unlock();
69     }
70 
71     res = arg->rwlock->TryWriteLock();
72     if (res) {
73         (*(arg->index))++;
74         arg->rwlock->Unlock();
75     }
76     return nullptr;
77 }
78 
TestThread4(void *input)79 static void *TestThread4(void *input)
80 {
81     ASSERT(input != nullptr);
82     ThreadMutexArgs *arg = reinterpret_cast<ThreadMutexArgs *>(input);
83     bool res = arg->lock->TryLockWithSpinning();
84     if (res) {
85         (*(arg->index))++;
86         arg->lock->Unlock();
87     }
88     return nullptr;
89 }
90 
TestThread5(void *input)91 static void *TestThread5(void *input)
92 {
93     ASSERT(input != nullptr);
94     ThreadRWLockArgs *arg = reinterpret_cast<ThreadRWLockArgs *>(input);
95     arg->rwlock->WriteLock();
96     arg->rwlock->Unlock();
97     return nullptr;
98 }
99 
HWTEST_F(MutexTest, RWLockTest, testing::ext::TestSize.Level0)100 HWTEST_F(MutexTest, RWLockTest, testing::ext::TestSize.Level0)
101 {
102     size_t res;
103     auto *rwlock = new os::memory::RWLock();
104     ThreadRWLockArgs arg = {rwlock, &res};
105 
106     pthread_t t[N + N];
107 
108     for (size_t i = 0; i < N; i++) {
109         pthread_create(&t[i], nullptr, TestThread1, reinterpret_cast<void *>(&arg));
110     }
111     for (size_t i = 0; i < N; i++) {
112         pthread_create(&t[i + N], nullptr, TestThread2, reinterpret_cast<void *>(&arg));
113     }
114 
115     for (size_t i = 0; i < N; i++) {
116         pthread_join(t[i], nullptr);
117         pthread_join(t[i + N], nullptr);
118     }
119 
120     rwlock->WriteLock();
121     rwlock->Unlock();
122 
123     ASSERT_EQ(res, N);
124     delete rwlock;
125 }
126 
HWTEST_F(MutexTest, TryLockTest, testing::ext::TestSize.Level0)127 HWTEST_F(MutexTest, TryLockTest, testing::ext::TestSize.Level0)
128 {
129     auto *rwlock = new os::memory::RWLock();
130     size_t res = 0;
131     ThreadRWLockArgs arg = {rwlock, &res};
132 
133     pthread_t t1;
134     rwlock->WriteLock();
135     pthread_create(&t1, nullptr, TestThread3, reinterpret_cast<void *>(&arg));
136     pthread_join(t1, nullptr);
137     rwlock->Unlock();
138     ASSERT_EQ(res, 0U);
139 
140     pthread_t t2;
141     rwlock->ReadLock();
142     res = 0;
143     pthread_create(&t2, nullptr, TestThread3, reinterpret_cast<void *>(&arg));
144     pthread_join(t2, nullptr);
145     rwlock->Unlock();
146     ASSERT_EQ(res, 1U);
147 
148     pthread_t t3;
149     res = 0;
150     pthread_create(&t3, nullptr, TestThread3, reinterpret_cast<void *>(&arg));
151     pthread_join(t3, nullptr);
152     ASSERT_EQ(res, 2U);
153     delete rwlock;
154 }
155 
HWTEST_F(MutexTest, TryLockWithSpiningTest, testing::ext::TestSize.Level0)156 HWTEST_F(MutexTest, TryLockWithSpiningTest, testing::ext::TestSize.Level0)
157 {
158     auto *lock = new os::memory::Mutex();
159     bool res = lock->TryLockWithSpinning();
160     ASSERT_TRUE(res);
161 
162     size_t index = 0;
163     ThreadMutexArgs arg = {lock, &index};
164     pthread_t t;
165     pthread_create(&t, nullptr, TestThread4, reinterpret_cast<void *>(&arg));
166     pthread_join(t, nullptr);
167     ASSERT_EQ(index, 0U);
168 
169     if (res) {
170         lock->Unlock();
171     }
172     delete lock;
173 }
174 
HWTEST_F(MutexTest, LockForOtherTest, testing::ext::TestSize.Level0)175 HWTEST_F(MutexTest, LockForOtherTest, testing::ext::TestSize.Level0)
176 {
177     auto *rwlock = new os::memory::RWLock();
178     ThreadRWLockArgs arg = {rwlock, nullptr};
179     rwlock->WriteLock();
180     auto *lock = new os::memory::Mutex();
181     pthread_t t;
182     pthread_create(&t, nullptr, TestThread5, reinterpret_cast<void *>(&arg));
183     lock->LockForOther(t);
184     bool res = lock->TryLock();
185     if (res) {
186         lock->Unlock();
187     }
188     lock->UnlockForOther(t);
189     rwlock->Unlock();
190     ASSERT_FALSE(res);
191     pthread_join(t, nullptr);
192     delete lock;
193 }
194 
195 }  // namespace panda
196