summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/concurrency.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/concurrency.cc')
-rw-r--r--test/syscalls/linux/concurrency.cc124
1 files changed, 124 insertions, 0 deletions
diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc
new file mode 100644
index 000000000..2c13b315c
--- /dev/null
+++ b/test/syscalls/linux/concurrency.cc
@@ -0,0 +1,124 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <signal.h>
+#include <atomic>
+
+#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
+
+namespace gvisor {
+namespace testing {
+namespace {
+
+// Test that a thread that never yields to the OS does not prevent other threads
+// from running.
+TEST(ConcurrencyTest, SingleProcessMultithreaded) {
+ std::atomic<int> a(0);
+
+ ScopedThread t([&a]() {
+ while (!a.load()) {
+ }
+ });
+
+ absl::SleepFor(absl::Seconds(1));
+
+ // We are still able to execute code in this thread. The other hasn't
+ // permanently hung execution in both threads.
+ a.store(1);
+}
+
+// Test that multiple threads in this process continue to execute in parallel,
+// even if an unrelated second process is spawned.
+TEST(ConcurrencyTest, MultiProcessMultithreaded) {
+ // In PID 1, start TIDs 1 and 2, and put both to sleep.
+ //
+ // Start PID 3, which spins for 5 seconds, then exits.
+ //
+ // TIDs 1 and 2 wake and attempt to Activate, which cannot occur until PID 3
+ // exits.
+ //
+ // Both TIDs 1 and 2 should be woken. If they are not both woken, the test
+ // hangs.
+ //
+ // This is all fundamentally racy. If we are failing to wake all threads, the
+ // expectation is that this test becomes flaky, rather than consistently
+ // failing.
+ //
+ // If additional background threads fail to block, we may never schedule the
+ // child, at which point this test effectively becomes
+ // MultiProcessConcurrency. That's not expected to occur.
+
+ std::atomic<int> a(0);
+ ScopedThread t([&a]() {
+ // Block so that PID 3 can execute and we can wait on its exit.
+ absl::SleepFor(absl::Seconds(1));
+ while (!a.load()) {
+ }
+ });
+
+ pid_t child_pid;
+ ASSERT_THAT(child_pid = fork(), SyscallSucceeds());
+ if (child_pid == 0) {
+ // Busy wait without making any blocking syscalls.
+ auto end = absl::Now() + absl::Seconds(5);
+ while (absl::Now() < end) {
+ }
+ _exit(0);
+ }
+
+ absl::SleepFor(absl::Seconds(1));
+
+ // If only TID 1 is woken, thread.Join will hang.
+ // If only TID 2 is woken, both will hang.
+ a.store(1);
+ t.Join();
+
+ int status = 0;
+ EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds());
+ EXPECT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(WEXITSTATUS(status), 0);
+}
+
+// Test that multiple processes can execute concurrently, even if one process
+// never yields.
+TEST(ConcurrencyTest, MultiProcessConcurrency) {
+
+ pid_t child_pid;
+ ASSERT_THAT(child_pid = fork(), SyscallSucceeds());
+ if (child_pid == 0) {
+ while (true) {
+ }
+ __builtin_unreachable();
+ }
+
+ absl::SleepFor(absl::Seconds(5));
+
+ // We are still able to execute code in this process. The other hasn't
+ // permanently hung execution in both processes.
+ ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds());
+ int status = 0;
+
+ ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds());
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), SIGKILL);
+}
+
+} // namespace
+} // namespace testing
+} // namespace gvisor