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