// Copyright 2018 The gVisor Authors. // // 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 "benchmark/benchmark.h" #include "test/util/platform_util.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. Regression test for // b/32119508. 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 = fork(); if (child_pid == 0) { // Busy wait without making any blocking syscalls. auto end = absl::Now() + absl::Seconds(5); while (absl::Now() < end) { } _exit(0); } ASSERT_THAT(child_pid, SyscallSucceeds()); 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) { SKIP_IF(PlatformSupportMultiProcess() == PlatformSupport::NotSupported); pid_t child_pid = fork(); if (child_pid == 0) { while (true) { int x = 0; benchmark::DoNotOptimize(x); // Don't optimize this loop away. } } ASSERT_THAT(child_pid, SyscallSucceeds()); 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