• 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_DEADLINE_MANAGER_H_
16 #define SANDBOXED_API_SANDBOX2_UTIL_DEADLINE_MANAGER_H_
17 
18 #include <cstddef>
19 #include <memory>
20 
21 #include "absl/base/no_destructor.h"
22 #include "absl/base/thread_annotations.h"
23 #include "absl/container/btree_set.h"
24 #include "absl/flags/declare.h"
25 #include "absl/functional/function_ref.h"
26 #include "absl/strings/string_view.h"
27 #include "absl/synchronization/mutex.h"
28 #include "absl/time/time.h"
29 #include "sandboxed_api/util/thread.h"
30 
31 ABSL_DECLARE_FLAG(int, sandbox2_deadline_manager_signal);
32 
33 namespace sandbox2 {
34 
35 class DeadlineManager;
36 
37 // Interface for managing the deadline for a blocking syscall. The syscall
38 // should be interruptible by a signal. On deadline expiration deadline manager
39 // repeatedly sends a signal to the thread running potentially running the
40 // blocking syscall until the provided functor returns. This repetition resolves
41 // the race between signaling syscall execution and actually invoking the
42 // blocking syscall.
43 //
44 // If the deadline is unlikely to be changed between multiple blocking syscalls,
45 // it's more efficient to reuse a single registration object.
46 class DeadlineRegistration {
47  public:
48   explicit DeadlineRegistration(DeadlineManager& manager);
49 
50   DeadlineRegistration(const DeadlineRegistration&) = delete;
51   DeadlineRegistration& operator=(const DeadlineRegistration&) = delete;
52 
53   // ANDROID: move assignment operator of 'DeadlineRegistration' is implicitly
54   // deleted because field 'manager_' is of reference type 'DeadlineManager &'
55   DeadlineRegistration(DeadlineRegistration&&) = delete;
56   DeadlineRegistration& operator=(DeadlineRegistration&&) = delete;
57 
58   ~DeadlineRegistration();
59 
60   // Executes a blocking syscall.
61   // The function is executed only if the deadline is not expired already.
62   // The syscall will be interrupted after the deadline.
63   void ExecuteBlockingSyscall(absl::FunctionRef<void()> blocking_fn);
64 
65   // Sets the deadline for the blocking syscall.
66   // The deadline is rounded up to the next resolution boundary.
67   // Can be called from a different thread concurrently with a running blocking
68   // syscall.
69   void SetDeadline(absl::Time deadline);
70 
71  private:
72   friend class DeadlineManager;
73 
74   struct Data {
75     absl::Mutex mutex;
76     // Changed only under both DeadlineManager::queue_mutex_ and Data::mutex.
77     absl::Time deadline = absl::InfiniteFuture();
78     pid_t ABSL_GUARDED_BY(mutex) tid = -1;
79     bool ABSL_GUARDED_BY(mutex) in_blocking_fn = false;
80     int ABSL_GUARDED_BY(mutex) notification_attempt = 0;
81   };
82 
83   DeadlineManager& manager_;
84   absl::Time last_deadline_ = absl::InfiniteFuture();
85   std::unique_ptr<Data> data_ = std::make_unique<Data>();
86 };
87 
88 // Engine for delivering deadline notifications to threads. Runs a separate
89 // thread which manages all the registered deadlines.
90 // All deadlines are rounded up to resolution of DeadlineManager (10 ms) for the
91 // purpose of batching notifications and reducing wakeups of the manager thread.
92 class DeadlineManager {
93  public:
94   // Returns the global instance of the deadline manager.
instance()95   static DeadlineManager& instance() {
96     static absl::NoDestructor<DeadlineManager> manager{
97         "deadline_manager-global"};
98     return *manager;
99   }
100   // Creates and starts the manager.
101   explicit DeadlineManager(absl::string_view name);
102 
103   DeadlineManager(const DeadlineManager&) = delete;
104   DeadlineManager& operator=(const DeadlineManager&) = delete;
105 
106   DeadlineManager(DeadlineManager&&) = delete;
107   DeadlineManager& operator=(DeadlineManager&&) = delete;
108 
109   ~DeadlineManager();
110 
111   // Adjusts the deadline for the registration.
112   // Prefer to use DeadlineRegistration::SetDeadline.
113   void AdjustDeadline(DeadlineRegistration& registration, absl::Time deadline);
114 
115  private:
116   friend class DeadlineRegistration;
117   static constexpr absl::Duration kResolution = absl::Milliseconds(10);
118 
119   struct ByDeadline {
operatorByDeadline120     bool operator()(const DeadlineRegistration::Data* a,
121                     const DeadlineRegistration::Data* b) const {
122       return a->deadline < b->deadline || (a->deadline == b->deadline && a < b);
123     }
124   };
125 
126   static void RegisterSignalHandler();
127   static void SignalHandler(int signal);
128   static void VerifySignalHandler();
129 
Register(DeadlineRegistration & registration)130   void Register(DeadlineRegistration& registration) {
131     absl::MutexLock lock(&registration_mutex_);
132     ++registered_deadlines_;
133   }
Unregister(DeadlineRegistration & registration)134   void Unregister(DeadlineRegistration& registration) {
135     {
136       absl::MutexLock lock(&queue_mutex_);
137       queue_.erase(registration.data_.get());
138     }
139     absl::MutexLock lock(&registration_mutex_);
140     --registered_deadlines_;
141   }
142   void Run();
143 
144   static int signal_nr_;
145   sapi::Thread thread_;
146   absl::Mutex queue_mutex_;
147   bool cancelled_ ABSL_GUARDED_BY(queue_mutex_) = false;
148   // We only need an adjustable heap, but there is no widely available
149   // implementation ¯\_(ツ)_/¯. Asymptotically it's the same and it should not
150   // matter at our scale.
151   absl::btree_set<DeadlineRegistration::Data*, ByDeadline> ABSL_GUARDED_BY(
152       queue_mutex_) queue_;
153   absl::Mutex registration_mutex_;
154   size_t registered_deadlines_ ABSL_GUARDED_BY(registration_mutex_) = 0;
155 };
156 
157 }  // namespace sandbox2
158 
159 #endif  // SANDBOXED_API_SANDBOX2_UTIL_DEADLINE_MANAGER_H_
160