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 <libpandabase/mem/ringbuf/lock_free_ring_buffer.h>
18 #include <queue>
19
20 namespace panda::mem {
21 static constexpr size_t ITERATIONS = 1000000;
22
23 #ifdef PANDA_NIGHTLY_TEST_ON
24 const uint64_t SEED = std::time(NULL);
25 #else
26 const uint64_t SEED = 1234;
27 #endif
28
29 constexpr auto DEFAULT_BUFFER_SIZE = 1024;
30
TEST(LockFreeRingBufferTest,EmptyTest)31 TEST(LockFreeRingBufferTest, EmptyTest)
32 {
33 LockFreeBuffer<size_t, DEFAULT_BUFFER_SIZE> buffer;
34 ASSERT_TRUE(buffer.IsEmpty());
35
36 buffer.Push(123);
37 ASSERT_FALSE(buffer.IsEmpty());
38 buffer.Pop();
39 ASSERT_TRUE(buffer.IsEmpty());
40 }
41
TEST(LockFreeRingBufferTest,FullTest)42 TEST(LockFreeRingBufferTest, FullTest)
43 {
44 LockFreeBuffer<size_t, DEFAULT_BUFFER_SIZE> buffer;
45 for (size_t i = 0; i < DEFAULT_BUFFER_SIZE - 1; i++) {
46 buffer.Push(i);
47 }
48 // in buffer can be maximum RING_BUFFER_SIZE - 1 elements
49 ASSERT_FALSE(buffer.TryPush(666));
50 buffer.Pop();
51 ASSERT_TRUE(buffer.TryPush(666));
52 }
53
TEST(LockFreeRingBufferTest,PushPopTest)54 TEST(LockFreeRingBufferTest, PushPopTest)
55 {
56 srand(SEED);
57 LockFreeBuffer<size_t, DEFAULT_BUFFER_SIZE> buffer;
58 std::queue<size_t> queue;
59 for (size_t i = 0; i < DEFAULT_BUFFER_SIZE - 1; i++) {
60 buffer.Push(i);
61 queue.push(i);
62 if (i % ((rand() % 100) + 1) == 0 && !queue.empty()) {
63 size_t buffer_pop = buffer.Pop();
64 size_t queue_pop = queue.front();
65 queue.pop();
66 ASSERT_EQ(buffer_pop, queue_pop);
67 }
68 }
69 while (!queue.empty()) {
70 size_t buffer_pop = buffer.Pop();
71 size_t queue_pop = queue.front();
72 queue.pop();
73 ASSERT_EQ(buffer_pop, queue_pop);
74 }
75 size_t x = 0;
76 bool pop = buffer.TryPop(&x);
77 ASSERT_FALSE(pop);
78 ASSERT_EQ(x, 0);
79 }
80
PopElementsFromBuffer(LockFreeBuffer<size_t,DEFAULT_BUFFER_SIZE> * buffer,std::atomic<bool> * pop_thread_started,std::atomic<bool> * pop_thread_finished,size_t * pop_sum)81 void PopElementsFromBuffer(LockFreeBuffer<size_t, DEFAULT_BUFFER_SIZE> *buffer, std::atomic<bool> *pop_thread_started,
82 std::atomic<bool> *pop_thread_finished, size_t *pop_sum)
83 {
84 pop_thread_started->store(true);
85 ASSERT(*pop_sum == 0);
86
87 while (!pop_thread_finished->load()) {
88 size_t x;
89 bool pop_success = buffer->TryPop(&x);
90 if (pop_success) {
91 *pop_sum += x;
92 }
93 }
94 }
95
TEST(LockFreeRingBufferTest,MultiThreadingTest)96 TEST(LockFreeRingBufferTest, MultiThreadingTest)
97 {
98 srand(SEED);
99 LockFreeBuffer<size_t, DEFAULT_BUFFER_SIZE> buffer;
100 std::atomic<bool> pop_thread_started = false;
101 std::atomic<bool> pop_thread_finished = false;
102 size_t pop_sum = 0;
103 auto pop_thread = std::thread(PopElementsFromBuffer, &buffer, &pop_thread_started, &pop_thread_finished, &pop_sum);
104 // wait until pop_thread starts to work
105 while (!pop_thread_started.load()) {
106 }
107
108 size_t expected_sum = 0;
109 size_t sum = 0;
110 for (size_t i = 0; i < ITERATIONS; i++) {
111 expected_sum += i;
112 buffer.Push(i);
113 }
114
115 // wait pop_thread to process everything
116 while (!buffer.IsEmpty()) {
117 }
118 pop_thread_finished.store(true);
119 pop_thread.join();
120 sum += pop_sum; // can be without atomics because we use it after .join only -> HB
121 ASSERT_EQ(sum, expected_sum);
122 }
123 } // namespace panda::mem
124