Skip to content

Commit 69b870d

Browse files
authored
semaphore_test.cc: add TimedWaitSpuriousWakeupLinux (#1037)
1 parent 59ddbce commit 69b870d

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

app/tests/semaphore_test.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@
1616

1717
#include "app/src/semaphore.h"
1818

19+
#include <atomic>
20+
1921
#include "app/src/thread.h"
2022
#include "app/src/time.h"
2123
#include "gmock/gmock.h"
2224
#include "gtest/gtest.h"
2325

26+
#if FIREBASE_PLATFORM_ANDROID || FIREBASE_PLATFORM_LINUX
27+
#include <pthread.h>
28+
29+
#include <csignal>
30+
#endif // #if FIREBASE_PLATFORM_ANDROID || FIREBASE_PLATFORM_LINUX
31+
2432
namespace {
2533

2634
// Basic test of TryWait, to make sure that its successes and failures
@@ -76,6 +84,50 @@ TEST(SemaphoreTest, TimedWait) {
7684
0.20 * firebase::internal::kMillisecondsPerSecond);
7785
}
7886

87+
#if FIREBASE_PLATFORM_ANDROID || FIREBASE_PLATFORM_LINUX
88+
// Use a global variable for `sigusr1_received` because there is no way to pass
89+
// a context to the signal handler (https://stackoverflow.com/q/64713130).
90+
std::atomic<bool> sigusr1_received;
91+
// Tests that Timed Wait handles spurious wakeup (Linux/Android specific).
92+
TEST(SemaphoreTest, TimedWaitSpuriousWakeupLinux) {
93+
// Register a dummy signal handler for SIGUSR1; otherwise, sending SIGUSR1
94+
// later on will crash the application.
95+
sigusr1_received.store(false);
96+
signal(SIGUSR1, [](int signum) { sigusr1_received.store(true); });
97+
98+
// Start a thread that will send SIGUSR1 to this thread in a few moments.
99+
pthread_t main_thread = pthread_self();
100+
firebase::Thread signal_sending_thread = firebase::Thread(
101+
[](void* arg) {
102+
firebase::internal::Sleep(500);
103+
pthread_kill(*static_cast<pthread_t*>(arg), SIGUSR1);
104+
},
105+
&main_thread);
106+
107+
// Call Semaphore::TimedWait() and keep track of how long it blocks for.
108+
firebase::Semaphore sem(0);
109+
auto start_ms = firebase::internal::GetTimestamp();
110+
const int kTimedWaitTimeout = 2 * firebase::internal::kMillisecondsPerSecond;
111+
EXPECT_FALSE(sem.TimedWait(kTimedWaitTimeout));
112+
auto finish_ms = firebase::internal::GetTimestamp();
113+
const int actual_wait_time = static_cast<int>(finish_ms - start_ms);
114+
EXPECT_TRUE(sigusr1_received.load());
115+
116+
// Wait for the "signal sending" thread to terminate, since it references
117+
// memory on the stack and we can't have it running after this method returns.
118+
signal_sending_thread.Join();
119+
120+
// Unregister the signal handler for SIGUSR1, since it's no longer needed.
121+
signal(SIGUSR1, NULL);
122+
123+
// Make sure that Semaphore::TimedWait() blocked for the entire timeout, and,
124+
// specifically, did NOT return early as a result of the SIGUSR1 interruption.
125+
const double kWaitTimeErrorMargin =
126+
0.20 * firebase::internal::kMillisecondsPerSecond;
127+
ASSERT_NEAR(actual_wait_time, kTimedWaitTimeout, kWaitTimeErrorMargin);
128+
}
129+
#endif // #if FIREBASE_PLATFORM_ANDROID || FIREBASE_PLATFORM_LINUX
130+
79131
TEST(SemaphoreTest, MultithreadedStressTest) {
80132
for (int i = 0; i < 10000; ++i) {
81133
firebase::Semaphore sem(0);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy