gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/timer_util.h (about) 1 // Copyright 2018 The gVisor Authors. 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 // http://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 GVISOR_TEST_UTIL_TIMER_UTIL_H_ 16 #define GVISOR_TEST_UTIL_TIMER_UTIL_H_ 17 18 #include <errno.h> 19 #ifdef __linux__ 20 #include <sys/syscall.h> 21 #endif 22 #include <sys/time.h> 23 24 #include <functional> 25 26 #include "gmock/gmock.h" 27 #include "absl/time/time.h" 28 #include "test/util/cleanup.h" 29 #include "test/util/logging.h" 30 #include "test/util/posix_error.h" 31 #include "test/util/test_util.h" 32 33 namespace gvisor { 34 namespace testing { 35 36 // From Linux's include/uapi/asm-generic/siginfo.h. 37 #ifndef sigev_notify_thread_id 38 #define sigev_notify_thread_id _sigev_un._tid 39 #endif 40 41 // Returns the current time. 42 absl::Time Now(clockid_t id); 43 44 // MonotonicTimer is a simple timer that uses a monotonic clock. 45 class MonotonicTimer { 46 public: 47 MonotonicTimer() {} 48 absl::Duration Duration() { 49 struct timespec ts; 50 TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); 51 return absl::TimeFromTimespec(ts) - start_; 52 } 53 54 void Start() { 55 struct timespec ts; 56 TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); 57 start_ = absl::TimeFromTimespec(ts); 58 } 59 60 protected: 61 absl::Time start_; 62 }; 63 64 // Sets the given itimer and returns a cleanup function that restores the 65 // previous itimer when it goes out of scope. 66 inline PosixErrorOr<Cleanup> ScopedItimer(int which, 67 struct itimerval const& new_value) { 68 struct itimerval old_value; 69 int rc = setitimer(which, &new_value, &old_value); 70 MaybeSave(); 71 if (rc < 0) { 72 return PosixError(errno, "setitimer failed"); 73 } 74 return Cleanup(std::function<void(void)>([which, old_value] { 75 EXPECT_THAT(setitimer(which, &old_value, nullptr), SyscallSucceeds()); 76 })); 77 } 78 79 #ifdef __linux__ 80 81 // RAII type for a kernel "POSIX" interval timer. (The kernel provides system 82 // calls such as timer_create that behave very similarly, but not identically, 83 // to those described by timer_create(2); in particular, the kernel does not 84 // implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on 85 // these kernel interval timers.) 86 // 87 // Compare implementation to FileDescriptor. 88 class IntervalTimer { 89 public: 90 IntervalTimer() = default; 91 92 explicit IntervalTimer(int id) { set_id(id); } 93 94 IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {} 95 96 IntervalTimer& operator=(IntervalTimer&& orig) { 97 if (this == &orig) return *this; 98 reset(orig.release()); 99 return *this; 100 } 101 102 IntervalTimer(const IntervalTimer& other) = delete; 103 IntervalTimer& operator=(const IntervalTimer& other) = delete; 104 105 ~IntervalTimer() { reset(); } 106 107 int get() const { return id_; } 108 109 int release() { 110 int const id = id_; 111 id_ = -1; 112 return id; 113 } 114 115 void reset() { reset(-1); } 116 117 void reset(int id) { 118 if (id_ >= 0) { 119 TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0); 120 MaybeSave(); 121 } 122 set_id(id); 123 } 124 125 PosixErrorOr<struct itimerspec> Set( 126 int flags, const struct itimerspec& new_value) const { 127 struct itimerspec old_value = {}; 128 if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) { 129 return PosixError(errno, "timer_settime"); 130 } 131 MaybeSave(); 132 return old_value; 133 } 134 135 PosixErrorOr<struct itimerspec> Get() const { 136 struct itimerspec curr_value = {}; 137 if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) { 138 return PosixError(errno, "timer_gettime"); 139 } 140 MaybeSave(); 141 return curr_value; 142 } 143 144 PosixErrorOr<int> Overruns() const { 145 int rv = syscall(SYS_timer_getoverrun, id_); 146 if (rv < 0) { 147 return PosixError(errno, "timer_getoverrun"); 148 } 149 MaybeSave(); 150 return rv; 151 } 152 153 private: 154 void set_id(int id) { id_ = std::max(id, -1); } 155 156 // Kernel timer_t is int; glibc timer_t is void*. 157 int id_ = -1; 158 }; 159 160 // A wrapper around timer_create(2). 161 PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid, 162 const struct sigevent& sev); 163 164 #endif // __linux__ 165 166 } // namespace testing 167 } // namespace gvisor 168 169 #endif // GVISOR_TEST_UTIL_TIMER_UTIL_H_