Skip to content

semaphore_test.cc: add TimedWaitSpuriousWakeupLinux #1037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 26, 2022
Merged
49 changes: 49 additions & 0 deletions app/tests/semaphore_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"

// Set SEMAPHORE_LINUX to reflect whether the platform is "Linux", using the
// same logic that semaphore.h uses.
#if FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || \
FIREBASE_PLATFORM_TVOS || FIREBASE_PLATFORM_WINDOWS
#define SEMAPHORE_LINUX 0
#else
#define SEMAPHORE_LINUX 1
#include <pthread.h>

#include <csignal>
#endif

namespace {

// Basic test of TryWait, to make sure that its successes and failures
Expand Down Expand Up @@ -76,6 +88,43 @@ TEST(SemaphoreTest, TimedWait) {
0.20 * firebase::internal::kMillisecondsPerSecond);
}

#if SEMAPHORE_LINUX
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about just using #ifdef FIREBASE_ANDROID || FIREBASE_LINUX here and above? To me that's clearer, even though it's not the same as the check in semaphore.h.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Tests that Timed Wait handles spurious wakeup (Linux/Android specific).
TEST(SemaphoreTest, TimedWaitSpuriousWakeupLinux) {
// Register a dummy signal handler for SIGUSR1; otherwise, sending SIGUSR1
// later on will crash the application.
signal(SIGUSR1, [](int signum) -> void {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this actually need the trailing return type syntax?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it does not. I've removed the trailing return.


// Start a thread that will send SIGUSR1 to this thread in a few moments.
pthread_t main_thread = pthread_self();
firebase::Thread signal_sending_thread = firebase::Thread(
[](void* arg) {
firebase::internal::Sleep(500);
pthread_kill(*static_cast<pthread_t*>(arg), SIGUSR1);
},
&main_thread);

// Call Semaphore::TimedWait() and keep track of how long it blocks for.
firebase::Semaphore sem(0);
int64_t start_ms = firebase::internal::GetTimestamp();
EXPECT_FALSE(sem.TimedWait(2 * firebase::internal::kMillisecondsPerSecond));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 * firebase::internal::kMillisecondsPerSecond should be in a constant in this function, for clarity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

int64_t finish_ms = firebase::internal::GetTimestamp();

// Wait for the "signal sending" thread to terminate, since it references
// memory on the stack and we can't have it running after this method returns.
signal_sending_thread.Join();

// Unregister the signal handler for SIGUSR1, since it's no longer needed.
signal(SIGUSR1, NULL);

// Make sure that Semaphore::TimedWait() blocked for the entire timeout, and,
// specifically, did NOT return early as a result of the SIGUSR1 interruption.
ASSERT_LT(labs((finish_ms - start_ms) -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just use EXPECT_NEAR here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, EXPECT_NEAR works perfectly. Done.

(2 * firebase::internal::kMillisecondsPerSecond)),
0.20 * firebase::internal::kMillisecondsPerSecond);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The allowed delta should probably also be in a constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
#endif // #if SEMAPHORE_LINUX

TEST(SemaphoreTest, DISABLED_MultithreadedStressTest) {
for (int i = 0; i < 10000; ++i) {
firebase::Semaphore sem(0);
Expand Down