From c5634b6e2cbdd6d37a056dca6ca87da0ad031525 Mon Sep 17 00:00:00 2001
From: Nadav Har'El <nyh@cloudius-systems.com>
Date: Sun, 26 Jan 2014 15:34:35 +0200
Subject: [PATCH] clock: Support monotonic clock in timerfd

Switch the timerfd to use std::chrono types, and add support also for the
monotonic clock.

Fixes #142.

Reviewed-by: Glauber Costa <glommer@cloudius-systems.com>
Signed-off-by: Nadav Har'El <nyh@cloudius-systems.com>
Signed-off-by: Pekka Enberg <penberg@cloudius-systems.com>
---
 libc/timerfd.cc | 53 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/libc/timerfd.cc b/libc/timerfd.cc
index da98f0940..ab67c08ef 100644
--- a/libc/timerfd.cc
+++ b/libc/timerfd.cc
@@ -49,12 +49,19 @@ private:
     bool _wakeup_thread_exit = false;
     condvar _blocked_reader;
     void wakeup_thread_func();
+
+    // Which clock to use to interpret s64 times.
+    int _clockid;
+    void set_timer(sched::timer &tmr, s64 time);
+public:
+    s64 time_now() const;
 };
 
 timerfd::timerfd(int clockid, int oflags)
     : special_file(FREAD | oflags, DTYPE_UNSPEC),
       _wakeup_thread(
-            [&] { wakeup_thread_func(); }, sched::thread::attr().stack(4096))
+            [&] { wakeup_thread_func(); }, sched::thread::attr().stack(4096)),
+      _clockid(clockid)
 {
     _wakeup_thread.start();
 }
@@ -67,13 +74,43 @@ timerfd::~timerfd() {
     _wakeup_thread.join();
 }
 
+void timerfd::set_timer(sched::timer &tmr, s64 t)
+{
+    using namespace osv::clock;
+    switch(_clockid) {
+    case CLOCK_REALTIME:
+        tmr.set(wall::time_point(std::chrono::nanoseconds(t)));
+        break;
+    case CLOCK_MONOTONIC:
+        tmr.set(uptime::time_point(std::chrono::nanoseconds(t)));
+        break;
+    default:
+        assert(false);
+    }
+}
+
+s64 timerfd::time_now() const
+{
+    using namespace std::chrono;
+    switch(_clockid) {
+    case CLOCK_REALTIME:
+        return duration_cast<nanoseconds>(
+                osv::clock::wall::now().time_since_epoch()).count();
+    case CLOCK_MONOTONIC:
+        return duration_cast<nanoseconds>(
+                osv::clock::uptime::now().time_since_epoch()).count();
+    default:
+        assert(false);
+    }
+}
+
 void timerfd::wakeup_thread_func()
 {
     sched::timer tmr(*sched::thread::current());
     WITH_LOCK(_mutex) {
         while (!_wakeup_thread_exit) {
             if (_wakeup_due != 0) {
-                tmr.set(_wakeup_due);
+                set_timer(tmr, _wakeup_due);
                 _wakeup_change_cond.wait(_mutex, &tmr);
                 if (tmr.expired()) {
                     _wakeup_due = 0;
@@ -109,7 +146,7 @@ void timerfd::get(s64 &expiration, s64 &interval) const
             if (!_interval) {
                 expiration = 0;
             } else {
-                auto now = nanotime();
+                auto now = time_now();
                 u64 count = (now - _expiration) / _interval;
                 expiration = _expiration + (count+1) * _interval;
             }
@@ -157,7 +194,7 @@ int timerfd::read(uio *data, int flags)
             ret = 1;
             _expiration = 0;
         } else {
-            auto now = nanotime();
+            auto now = time_now();
             // set next wakeup for the next multiple of interval from
             // _expiration which is after "now".
             assert (now >= _expiration);
@@ -195,10 +232,8 @@ int timerfd::close()
 
 int timerfd_create(int clockid, int flags) {
     switch (clockid) {
-    case CLOCK_MONOTONIC:
-        WARN_ONCE("timerfd_create() does not yet support CLOCK_MONOTONIC\n");
-        return libc_error(EINVAL);
     case CLOCK_REALTIME:
+    case CLOCK_MONOTONIC:
         // fine.
         break;
     default:
@@ -232,7 +267,7 @@ int timerfd_settime(int fd, int flags, const itimerspec *newval,
     }
 
     s64 expiration, interval;
-    s64 now = nanotime();
+    auto now = tf->time_now();
     if (oldval) {
         tf->get(expiration, interval);
         if (expiration) {
@@ -269,7 +304,7 @@ int timerfd_gettime(int fd, itimerspec *val)
     }
 
     s64 expiration, interval;
-    s64 now = nanotime();
+    auto now = tf->time_now();
     tf->get(expiration, interval);
     if (expiration) {
         // timerfd_gettime() wants relative time
-- 
GitLab