Skip to content

Commit 1e91228

Browse files
happyCoder92copybara-github
authored andcommitted
PidWaiter: Add a thread-safe Notify
PiperOrigin-RevId: 721291791 Change-Id: I115155e01c6f916f6881108a5cae9053887fdf79
1 parent 17044d1 commit 1e91228

File tree

5 files changed

+121
-7
lines changed

5 files changed

+121
-7
lines changed

sandboxed_api/sandbox2/util/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ cc_library(
159159
copts = sapi_platform_copts(),
160160
deps = [
161161
":deadline_manager",
162+
"@com_google_absl//absl/base:core_headers",
163+
"@com_google_absl//absl/cleanup",
164+
"@com_google_absl//absl/synchronization",
162165
"@com_google_absl//absl/time",
163166
],
164167
)
@@ -171,5 +174,6 @@ cc_test(
171174
":pid_waiter",
172175
"@com_google_absl//absl/time",
173176
"@com_google_googletest//:gtest_main",
177+
"@com_google_sandboxed_api//sandboxed_api/util:thread",
174178
],
175179
)

sandboxed_api/sandbox2/util/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ add_library(sandbox2_util_pid_waiter ${SAPI_LIB_TYPE}
6868
)
6969
add_library(sandbox2::pid_waiter ALIAS sandbox2_util_pid_waiter)
7070
target_link_libraries(sandbox2_util_pid_waiter
71-
PUBLIC absl::time
71+
PUBLIC absl::core_headers
72+
absl::cleanup
73+
absl::synchronization
74+
absl::time
7275
sandbox2::deadline_manager
7376
PRIVATE sapi::base
7477
)
@@ -166,6 +169,7 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING)
166169
absl::time
167170
sandbox2::pid_waiter
168171
sapi::test_main
172+
sapi::thread
169173
)
170174

171175
# sandboxed_api/sandbox2/util:deadline_manager_test

sandboxed_api/sandbox2/util/pid_waiter.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include <cerrno>
2020
#include <memory>
2121

22+
#include "absl/cleanup/cleanup.h"
23+
#include "absl/synchronization/mutex.h"
24+
#include "absl/time/time.h"
25+
#include "sandboxed_api/sandbox2/util/deadline_manager.h"
26+
2227
namespace sandbox2 {
2328

2429
namespace {
@@ -75,6 +80,10 @@ bool PidWaiter::CheckStatus(pid_t pid, bool blocking) {
7580
void PidWaiter::RefillStatuses() {
7681
constexpr int kMaxIterations = 1000;
7782
constexpr int kPriorityCheckPeriod = 100;
83+
absl::Cleanup notify = [this] {
84+
absl::MutexLock lock(&notify_mutex_);
85+
notified_ = false;
86+
};
7887
if (!statuses_.empty()) {
7988
return;
8089
}
@@ -88,8 +97,22 @@ void PidWaiter::RefillStatuses() {
8897
break;
8998
}
9099
}
91-
if (statuses_.empty() && deadline_registration_.has_value()) {
92-
deadline_registration_->ExecuteBlockingSyscall(
100+
if (statuses_.empty()) {
101+
DeadlineRegistration* deadline_registration = nullptr;
102+
{
103+
absl::MutexLock lock(&notify_mutex_);
104+
if (deadline_ == absl::InfinitePast() || notified_) {
105+
return;
106+
}
107+
if (!deadline_registration_.has_value()) {
108+
deadline_registration_.emplace(DeadlineManager::instance());
109+
}
110+
deadline_registration_->SetDeadline(deadline_);
111+
// DeadlineRegistration is only constructed once, so it's safe to use it
112+
// outside of the lock.
113+
deadline_registration = &*deadline_registration_;
114+
}
115+
deadline_registration->ExecuteBlockingSyscall(
93116
[&] { CheckStatus(-1, /*blocking=*/true); });
94117
}
95118
}

sandboxed_api/sandbox2/util/pid_waiter.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <optional>
2323
#include <utility>
2424

25+
#include "absl/base/thread_annotations.h"
26+
#include "absl/synchronization/mutex.h"
2527
#include "absl/time/time.h"
2628
#include "sandboxed_api/sandbox2/util/deadline_manager.h"
2729

@@ -58,21 +60,34 @@ class PidWaiter {
5860

5961
// Sets the deadline for the next Wait() call.
6062
void SetDeadline(absl::Time deadline) {
61-
if (!deadline_registration_.has_value()) {
62-
deadline_registration_.emplace(DeadlineManager::instance());
63+
absl::MutexLock lock(&notify_mutex_);
64+
deadline_ = deadline;
65+
}
66+
67+
// Breaks out of the current Wait operation, if there is one running
68+
// concurrently. Otherwise, makes the next Wait non-blocking.
69+
// Can be called concurrently with Wait and SetDeadline.
70+
void Notify() {
71+
absl::MutexLock lock(&notify_mutex_);
72+
if (deadline_registration_.has_value()) {
73+
deadline_registration_->SetDeadline(absl::InfinitePast());
6374
}
64-
deadline_registration_->SetDeadline(deadline);
75+
notified_ = true;
6576
}
6677

6778
private:
6879
bool CheckStatus(pid_t pid, bool blocking = false);
6980
void RefillStatuses();
7081

7182
pid_t priority_pid_;
72-
std::optional<DeadlineRegistration> deadline_registration_;
7383
std::deque<std::pair<pid_t, int>> statuses_;
7484
std::unique_ptr<WaitPidInterface> wait_pid_iface_;
7585
int last_errno_ = 0;
86+
absl::Mutex notify_mutex_;
87+
absl::Time deadline_ ABSL_GUARDED_BY(notify_mutex_) = absl::InfinitePast();
88+
std::optional<DeadlineRegistration> deadline_registration_
89+
ABSL_GUARDED_BY(notify_mutex_);
90+
bool notified_ ABSL_GUARDED_BY(notify_mutex_) = false;
7691
};
7792

7893
} // namespace sandbox2

sandboxed_api/sandbox2/util/pid_waiter_test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "gtest/gtest.h"
2424
#include "absl/time/clock.h"
2525
#include "absl/time/time.h"
26+
#include "sandboxed_api/util/thread.h"
2627

2728
namespace sandbox2 {
2829
namespace {
@@ -153,5 +154,72 @@ TEST(PidWaiterTest, DeadlineRespected) {
153154
EXPECT_EQ(errno, EINTR);
154155
}
155156

157+
TEST(PidWaiterTest, NotifyConcurrent) {
158+
auto mock_wait_pid = std::make_unique<MockWaitPid>();
159+
EXPECT_CALL(*mock_wait_pid,
160+
WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
161+
.WillRepeatedly(Return(0));
162+
EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
163+
.WillRepeatedly([](int pid, int* status, int flags) {
164+
struct timespec ts = absl::ToTimespec(absl::Seconds(2));
165+
if (nanosleep(&ts, nullptr) == -1) {
166+
return -1;
167+
}
168+
*status = kFirstPid;
169+
return kFirstPid;
170+
});
171+
PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
172+
waiter.SetDeadline(absl::Now() + absl::Seconds(1));
173+
sapi::Thread notify_thread([&] {
174+
absl::SleepFor(absl::Milliseconds(100));
175+
waiter.Notify();
176+
});
177+
int status;
178+
absl::Time start = absl::Now();
179+
EXPECT_EQ(waiter.Wait(&status), -1);
180+
EXPECT_LT(absl::Now() - start, absl::Milliseconds(500));
181+
EXPECT_EQ(errno, EINTR);
182+
}
183+
184+
TEST(PidWaiterTest, NotifyNext) {
185+
auto mock_wait_pid = std::make_unique<MockWaitPid>();
186+
EXPECT_CALL(*mock_wait_pid,
187+
WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
188+
.WillRepeatedly(Return(0));
189+
EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
190+
.Times(0);
191+
PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
192+
waiter.SetDeadline(absl::Now() + absl::Seconds(1));
193+
waiter.Notify();
194+
int status;
195+
absl::Time start = absl::Now();
196+
EXPECT_EQ(waiter.Wait(&status), 0);
197+
EXPECT_LT(absl::Now() - start, absl::Milliseconds(500));
198+
}
199+
200+
TEST(PidWaiterTest, DeadlineUnchangedAfterNotify) {
201+
auto mock_wait_pid = std::make_unique<MockWaitPid>();
202+
EXPECT_CALL(*mock_wait_pid,
203+
WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
204+
.WillRepeatedly(Return(0));
205+
EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
206+
.WillRepeatedly([](int pid, int* status, int flags) {
207+
struct timespec ts = absl::ToTimespec(absl::Milliseconds(500));
208+
if (nanosleep(&ts, nullptr) == -1) {
209+
return -1;
210+
}
211+
*status = kFirstPid;
212+
return kFirstPid;
213+
});
214+
PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
215+
waiter.SetDeadline(absl::Now() + absl::Milliseconds(900));
216+
waiter.Notify();
217+
int status;
218+
EXPECT_EQ(waiter.Wait(&status), 0);
219+
absl::SleepFor(absl::Milliseconds(500));
220+
EXPECT_EQ(waiter.Wait(&status), -1);
221+
EXPECT_EQ(errno, EINTR);
222+
}
223+
156224
} // namespace
157225
} // namespace sandbox2

0 commit comments

Comments
 (0)