• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
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 //     https://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 #ifndef SANDBOXED_API_SANDBOX2_UTIL_PID_WAITER_H_
16 #define SANDBOXED_API_SANDBOX2_UTIL_PID_WAITER_H_
17 
18 #include <sys/wait.h>
19 
20 #include <deque>
21 #include <memory>
22 #include <optional>
23 #include <utility>
24 
25 #include "absl/base/thread_annotations.h"
26 #include "absl/synchronization/mutex.h"
27 #include "absl/time/time.h"
28 #include "sandboxed_api/sandbox2/util/deadline_manager.h"
29 
30 namespace sandbox2 {
31 
32 // Since waitpid() is biased towards newer threads, we run the risk of
33 // starving older threads if the newer ones raise a lot of events. To avoid
34 // it, we use this class to gather all the waiting threads and then return
35 // them one at a time on each call to Wait(). In this way, everyone gets their
36 // chance.
37 class PidWaiter {
38  public:
39   // Interface for waitpid() to allow mocking it in tests.
40   class WaitPidInterface {
41    public:
42     virtual int WaitPid(pid_t pid, int* status, int flags) = 0;
43     virtual ~WaitPidInterface() = default;
44   };
45 
46   // Constructs a PidWaiter where the given priority_pid is checked first.
47   explicit PidWaiter(pid_t priority_pid = -1);
PidWaiter(pid_t priority_pid,std::unique_ptr<WaitPidInterface> wait_pid_iface)48   PidWaiter(pid_t priority_pid,
49             std::unique_ptr<WaitPidInterface> wait_pid_iface)
50       : priority_pid_(priority_pid),
51         wait_pid_iface_(std::move(wait_pid_iface)) {};
52 
53   // Returns the PID of a thread that needs attention, populating 'status'
54   // with the status returned by the waitpid() call. It returns 0 if no
55   // threads require attention at the moment, or -1 if there was an error, in
56   // which case the error value can be found in 'errno'.
57   int Wait(int* status);
58 
SetPriorityPid(pid_t pid)59   void SetPriorityPid(pid_t pid) { priority_pid_ = pid; }
60 
61   // Sets the deadline for the next Wait() call.
SetDeadline(absl::Time deadline)62   void SetDeadline(absl::Time deadline) {
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.
Notify()70   void Notify() {
71     absl::MutexLock lock(&notify_mutex_);
72     if (deadline_registration_.has_value()) {
73       deadline_registration_->SetDeadline(absl::InfinitePast());
74     }
75     notified_ = true;
76   }
77 
78  private:
79   bool CheckStatus(pid_t pid, bool blocking = false);
80   void RefillStatuses();
81 
82   pid_t priority_pid_;
83   std::deque<std::pair<pid_t, int>> statuses_;
84   std::unique_ptr<WaitPidInterface> wait_pid_iface_;
85   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;
91 };
92 
93 }  // namespace sandbox2
94 
95 #endif  // SANDBOXED_API_SANDBOX2_UTIL_PID_WAITER_H_
96