1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// 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
|