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(¬ify_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(¬ify_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