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
22namespace panda::test {
23
24class MutexTest : public testing::Test {};
25
26constexpr size_t N = 10;
27constexpr size_t WRITE_LOCK_SLEEP_TIME = 50;
28constexpr size_t READ_LOCK_SLEEP_TIME = 1000;
29
30struct ThreadRWLockArgs {
31    os::memory::RWLock *rwlock;
32    size_t *index;
33};
34
35struct ThreadMutexArgs {
36    os::memory::Mutex *lock;
37    size_t *index;
38};
39
40static 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
51static 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
61static 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
79static 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
91static 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
100HWTEST_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
127HWTEST_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
156HWTEST_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
175HWTEST_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