github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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_