1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gtest/gtest.h"
18 
19 #include <array>
20 #include <thread>
21 
22 #include "berberis/runtime_primitives/signal_queue.h"
23 
24 namespace berberis {
25 
26 namespace {
27 
TEST(SignalQueue,EnqueueDequeue)28 TEST(SignalQueue, EnqueueDequeue) {
29   SignalQueue q;
30   siginfo_t* p;
31 
32   EXPECT_EQ(nullptr, q.DequeueSignalUnsafe());
33 
34   p = q.AllocSignal();
35   p->si_signo = 11;
36   p->si_value.sival_int = 1;
37   q.EnqueueSignal(p);
38 
39   p = q.DequeueSignalUnsafe();
40   ASSERT_TRUE(p);
41   EXPECT_EQ(11, p->si_signo);
42   EXPECT_EQ(1, p->si_value.sival_int);
43   q.FreeSignal(p);
44 
45   EXPECT_EQ(nullptr, q.DequeueSignalUnsafe());
46 }
47 
TEST(SignalQueue,FIFO)48 TEST(SignalQueue, FIFO) {
49   SignalQueue q;
50   siginfo_t* p;
51 
52   p = q.AllocSignal();
53   p->si_signo = 11;
54   p->si_value.sival_int = 1;
55   q.EnqueueSignal(p);
56 
57   p = q.AllocSignal();
58   p->si_signo = 11;
59   p->si_value.sival_int = 2;
60   q.EnqueueSignal(p);
61 
62   p = q.AllocSignal();
63   p->si_signo = 11;
64   p->si_value.sival_int = 3;
65   q.EnqueueSignal(p);
66 
67   p = q.DequeueSignalUnsafe();
68   ASSERT_TRUE(p);
69   EXPECT_EQ(11, p->si_signo);
70   EXPECT_EQ(1, p->si_value.sival_int);
71   q.FreeSignal(p);
72 
73   p = q.DequeueSignalUnsafe();
74   ASSERT_TRUE(p);
75   EXPECT_EQ(11, p->si_signo);
76   EXPECT_EQ(2, p->si_value.sival_int);
77   q.FreeSignal(p);
78 
79   p = q.DequeueSignalUnsafe();
80   ASSERT_TRUE(p);
81   EXPECT_EQ(11, p->si_signo);
82   EXPECT_EQ(3, p->si_value.sival_int);
83   q.FreeSignal(p);
84 }
85 
TEST(SignalQueue,Priority)86 TEST(SignalQueue, Priority) {
87   SignalQueue q;
88   siginfo_t* p;
89 
90   p = q.AllocSignal();
91   p->si_signo = 11;
92   p->si_value.sival_int = 1;
93   q.EnqueueSignal(p);
94 
95   p = q.AllocSignal();
96   p->si_signo = 6;
97   p->si_value.sival_int = 2;
98   q.EnqueueSignal(p);
99 
100   p = q.AllocSignal();
101   p->si_signo = 11;
102   p->si_value.sival_int = 3;
103   q.EnqueueSignal(p);
104 
105   p = q.DequeueSignalUnsafe();
106   ASSERT_TRUE(p);
107   EXPECT_EQ(6, p->si_signo);
108   EXPECT_EQ(2, p->si_value.sival_int);
109   q.FreeSignal(p);
110 
111   p = q.DequeueSignalUnsafe();
112   ASSERT_TRUE(p);
113   EXPECT_EQ(11, p->si_signo);
114   EXPECT_EQ(1, p->si_value.sival_int);
115   q.FreeSignal(p);
116 
117   p = q.AllocSignal();
118   p->si_signo = 6;
119   p->si_value.sival_int = 4;
120   q.EnqueueSignal(p);
121 
122   p = q.DequeueSignalUnsafe();
123   ASSERT_TRUE(p);
124   EXPECT_EQ(6, p->si_signo);
125   EXPECT_EQ(4, p->si_value.sival_int);
126   q.FreeSignal(p);
127 
128   p = q.DequeueSignalUnsafe();
129   ASSERT_TRUE(p);
130   EXPECT_EQ(11, p->si_signo);
131   EXPECT_EQ(3, p->si_value.sival_int);
132   q.FreeSignal(p);
133 }
134 
135 const int kStressThreads = 40;
136 const int kStressIters = 40;
137 const int kStressPri = 10;
138 
StressEnqueueFunc(SignalQueue * q)139 void* StressEnqueueFunc(SignalQueue* q) {
140   for (int i = 0; i < kStressIters; ++i) {
141     for (int j = 0; j < kStressPri; ++j) {
142       siginfo_t* p = q->AllocSignal();
143       // Don't pass signo = 0, let's reserve it for special uses.
144       p->si_signo = j + 1;
145       q->EnqueueSignal(p);
146     }
147   }
148 
149   return nullptr;
150 }
151 
TEST(SignalQueue,Stress)152 TEST(SignalQueue, Stress) {
153   SignalQueue q;
154   std::array<std::thread, kStressThreads> threads;
155 
156   for (int i = 0; i < kStressThreads; ++i) {
157     threads[i] = std::thread(StressEnqueueFunc, &q);
158   }
159 
160   const int kEnqueueCount = kStressThreads * kStressIters * kStressPri;
161   int dequeue_count = 0;
162 
163   // Dequeue some signals.
164   // As they are added while consumed, we can't really check dequeue order.
165   while (dequeue_count < kEnqueueCount / 2) {
166     siginfo_t* p = q.DequeueSignalUnsafe();
167     // We can consume faster than signals are added, though very unlikely :)
168     if (p) {
169       q.FreeSignal(p);
170       ++dequeue_count;
171     }
172   }
173 
174   for (int i = 0; i < kStressThreads; ++i) {
175     threads[i].join();
176   }
177 
178   // Dequeue remaining signals.
179   // As we don't add signals any more, check dequeue order.
180   int pri = 0;
181   for (; dequeue_count < kEnqueueCount; ++dequeue_count) {
182     siginfo_t* p = q.DequeueSignalUnsafe();
183     ASSERT_TRUE(p);
184     EXPECT_LE(pri, p->si_signo);
185     pri = p->si_signo;
186     q.FreeSignal(p);
187   }
188 }
189 
190 }  // namespace
191 
192 }  // namespace berberis
193