gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/concurrency.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <signal.h> 16 #include <time.h> 17 18 #include <atomic> 19 20 #include "gtest/gtest.h" 21 #include "absl/strings/string_view.h" 22 #include "absl/time/clock.h" 23 #include "absl/time/time.h" 24 #include "benchmark/benchmark.h" 25 #include "test/util/platform_util.h" 26 #include "test/util/test_util.h" 27 #include "test/util/thread_util.h" 28 29 namespace gvisor { 30 namespace testing { 31 namespace { 32 33 // Test that a thread that never yields to the OS does not prevent other threads 34 // from running. 35 TEST(ConcurrencyTest, SingleProcessMultithreaded) { 36 std::atomic<int> a(0); 37 38 ScopedThread t([&a]() { 39 while (!a.load()) { 40 } 41 }); 42 43 absl::SleepFor(absl::Seconds(1)); 44 45 // We are still able to execute code in this thread. The other hasn't 46 // permanently hung execution in both threads. 47 a.store(1); 48 } 49 50 // Test that multiple threads in this process continue to execute in parallel, 51 // even if an unrelated second process is spawned. Regression test for 52 // b/32119508. 53 TEST(ConcurrencyTest, MultiProcessMultithreaded) { 54 // In PID 1, start TIDs 1 and 2, and put both to sleep. 55 // 56 // Start PID 3, which spins for 5 seconds, then exits. 57 // 58 // TIDs 1 and 2 wake and attempt to Activate, which cannot occur until PID 3 59 // exits. 60 // 61 // Both TIDs 1 and 2 should be woken. If they are not both woken, the test 62 // hangs. 63 // 64 // This is all fundamentally racy. If we are failing to wake all threads, the 65 // expectation is that this test becomes flaky, rather than consistently 66 // failing. 67 // 68 // If additional background threads fail to block, we may never schedule the 69 // child, at which point this test effectively becomes 70 // MultiProcessConcurrency. That's not expected to occur. 71 72 std::atomic<int> a(0); 73 ScopedThread t([&a]() { 74 // Block so that PID 3 can execute and we can wait on its exit. 75 absl::SleepFor(absl::Seconds(1)); 76 while (!a.load()) { 77 } 78 }); 79 80 pid_t child_pid = fork(); 81 if (child_pid == 0) { 82 struct timespec now; 83 TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0); 84 // Busy wait without making any blocking syscalls. 85 auto end = now.tv_sec + 5; 86 while (now.tv_sec < end) { 87 TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0); 88 } 89 _exit(0); 90 } 91 ASSERT_THAT(child_pid, SyscallSucceeds()); 92 93 absl::SleepFor(absl::Seconds(1)); 94 95 // If only TID 1 is woken, thread.Join will hang. 96 // If only TID 2 is woken, both will hang. 97 a.store(1); 98 t.Join(); 99 100 int status = 0; 101 EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); 102 EXPECT_TRUE(WIFEXITED(status)); 103 EXPECT_EQ(WEXITSTATUS(status), 0); 104 } 105 106 // Test that multiple processes can execute concurrently, even if one process 107 // never yields. 108 TEST(ConcurrencyTest, MultiProcessConcurrency) { 109 SKIP_IF(PlatformSupportMultiProcess() == PlatformSupport::NotSupported); 110 111 pid_t child_pid = fork(); 112 if (child_pid == 0) { 113 while (true) { 114 int x = 0; 115 benchmark::DoNotOptimize(x); // Don't optimize this loop away. 116 } 117 } 118 ASSERT_THAT(child_pid, SyscallSucceeds()); 119 120 absl::SleepFor(absl::Seconds(5)); 121 122 // We are still able to execute code in this process. The other hasn't 123 // permanently hung execution in both processes. 124 ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); 125 int status = 0; 126 127 ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); 128 ASSERT_TRUE(WIFSIGNALED(status)); 129 ASSERT_EQ(WTERMSIG(status), SIGKILL); 130 } 131 132 } // namespace 133 } // namespace testing 134 } // namespace gvisor