implement a basic time/clock datastructure
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Mon, 29 Jan 2018 10:06:13 +0000 (11:06 +0100)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Wed, 27 Mar 2019 09:31:38 +0000 (10:31 +0100)
Add a class ``Time'' to the timefunc part of the library. The
goal is to provide a generalist handle around struct timespec to
supersede the current ones that had been built on now deprecated
APIs (e. g. ``timeb.h'').

src/timefunc.cpp
src/timefunc.hxx
test/test_timefunc.cpp

index e8b1917..cab9469 100644 (file)
@@ -771,3 +771,125 @@ bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
 } // eo realtime_clock_gettime(long int&,long int&)
 
 
+namespace I2n {
+
+namespace clock {
+
+    namespace {
+
+        static inline clockid_t
+        clockid_of_flags (const enum type::id      id,
+                          const enum type::variant var)
+        {
+            clockid_t cid = CLOCK_MONOTONIC_COARSE;
+
+            switch (id) {
+
+                default:
+                case type::mono: {
+                    switch (var) {
+                        default: {
+                            break;
+                        }
+                        case type::raw: {
+                            cid = CLOCK_MONOTONIC_RAW;
+                            break;
+                        }
+                        case type::exact: {
+                            cid = CLOCK_MONOTONIC;
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case type::real: {
+                    if (var == type::exact) {
+                        cid = CLOCK_REALTIME;
+                    } else {
+                        cid = CLOCK_REALTIME_COARSE;
+                    }
+                    break;
+                }
+
+                case type::boot: {
+                    if (var & type::exact) {
+                        cid = CLOCK_BOOTTIME;
+                    }
+                    break;
+                }
+
+                case type::cpu: {
+                    if (var == type::thread) {
+                        cid = CLOCK_THREAD_CPUTIME_ID;
+                    } else {
+                        cid = CLOCK_PROCESS_CPUTIME_ID;
+                    }
+                    break;
+                }
+            } /* [switch id] */
+
+            return cid;
+        }
+
+        static const struct timespec zero_time = { 0, 0 };
+
+#       define NANO (1000L * 1000 * 1000)
+
+    } /* [namespace] */
+
+    Time::Time (const enum type::id id, const enum type::variant var)
+        : value   (zero_time)
+        , id      (id)
+        , variant (var)
+        , err     (0)
+    { }
+
+    int64_t
+    Time::as_nanosec (void)
+    { return int64_t (this->value.tv_sec) * NANO + this->value.tv_nsec; }
+
+    long
+    Time::as_nanosec_L (void) /* likely to overflow */
+    { return static_cast<long>(this->as_nanosec ()); }
+
+    void
+    Time::unset (void)
+    { this->value = zero_time; }
+
+    bool
+    Time::set (void)
+    {
+        struct timespec now;
+
+        errno = 0;
+        if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
+            == -1)
+        {
+            this->err = errno;
+            this->unset ();
+
+            return false;
+        }
+        this->err   = 0;
+        this->value = now;
+
+        return true;
+    }
+
+    boost::optional<Time>
+    now (const enum type::id id, const enum type::variant var)
+    {
+        Time ret (id);
+
+        if (!ret.set ()) {
+            return boost::none;
+        }
+
+        return ret;
+    }
+
+} /* [namespace clock] */
+
+} /* [namespace I2n] */
+
index 18b0314..eec351b 100644 (file)
@@ -26,9 +26,12 @@ on this file might be covered by the GNU General Public License.
 #ifndef __TIMEFUNC_HXX
 #define __TIMEFUNC_HXX
 
+#include <errno.h>
 #include <string>
 #include <list>
 
+#include <boost/optional.hpp>
+
 #include <week.hpp>
 
 double prec_time(void);
@@ -213,6 +216,97 @@ long long monotonic_clock_gettime_nano();
 
 bool realtime_clock_gettime(long int& seconds, long int& nano_seconds);
 
+namespace I2n {
+
+namespace clock {
+
+    namespace type {
+        /*
+         * represent the clock id options from clock_gettime(2) as
+         * a pair of enums. by default, CLOCK_MONOTONIC_COARSE is used
+         * everywhere since that’s what we want in most cases.
+         */
+        enum id {
+            mono,      /* CLOCK_MONOTONIC_COARSE */
+            real,      /* CLOCK_REALIME_COARSE */
+            boot,      /* CLOCK_BOOTTIME */
+            cpu,       /* CLOCK_CPUTIME_* */
+        };
+
+        /*
+         * for clocks that support it: non-coarse or raw variants; if a variant
+         * does not apply to a given clock, it is ignored
+         */
+        enum variant {
+            dflt,       /* all */
+            exact,      /* mono, real: not (*_COARSE) */
+            raw,        /* mono: _RAW */
+            process,    /* cpu: *_PROCESS_* */
+            thread,     /* cpu: *_THREAD_* */
+        };
+
+    } /* [namespace type] */
+
+
+    class Time {
+
+        private:
+            struct timespec           value;
+
+        public:
+            const enum type::id       id;
+            const enum type::variant  variant;
+            int                       err;
+
+        /* ctors *************************************************************/
+        public:
+
+            Time (const enum type::id      id  = type::mono,
+                  const enum type::variant var = type::dflt);
+
+        /* value access ******************************************************/
+        public:
+
+            inline const struct timespec &get_time (void)
+            { return this->value; }
+
+            inline const time_t &get_sec (void)
+            { return this->value.tv_sec; };
+
+            inline const long &get_nsec (void)
+            { return this->value.tv_nsec; }
+
+            int64_t as_nanosec (void);
+
+            long as_nanosec_L (void);
+
+        /* setting time ******************************************************/
+        public:
+
+            inline void
+            set (const struct timespec &ts)
+            { this->value = ts; };
+
+            inline void
+            set (const time_t sec, const long nsec)
+            {
+                this->value.tv_sec  = sec;
+                this->value.tv_nsec = nsec;
+            };
+
+            bool set (void);
+
+            void unset (void);
+
+    }; /* [class Time] */
+
+    boost::optional<Time>
+    now (const enum type::id      id  = type::mono,
+         const enum type::variant var = type::dflt);
+
+} /* [namespace clock] */
+
+} /* [namespace I2n] */
 
 
 #endif
index 58dcaf5..c973a4f 100644 (file)
@@ -461,5 +461,42 @@ BOOST_AUTO_TEST_CASE(DateToSeconds2)
     BOOST_CHECK_EQUAL(1341093600, date_to_seconds("2012-07-01"));
 }
 
+BOOST_AUTO_TEST_SUITE(Clock)
+
+    BOOST_AUTO_TEST_CASE(ctor_simple)
+    {
+        I2n::clock::Time t;
+
+        BOOST_CHECK_EQUAL(t.get_sec  (), 0);
+        BOOST_CHECK_EQUAL(t.get_nsec (), 0);
+    }
+
+    BOOST_AUTO_TEST_CASE(ctor_type)
+    {
+        I2n::clock::Time t (I2n::clock::type::real);
+
+        BOOST_CHECK_EQUAL(t.get_sec  (), 0);
+        BOOST_CHECK_EQUAL(t.get_nsec (), 0);
+    }
+
+    BOOST_AUTO_TEST_CASE(ctor_variant)
+    {
+        I2n::clock::Time t (I2n::clock::type::cpu,
+                            I2n::clock::type::thread);
+
+        BOOST_CHECK_EQUAL(t.get_sec  (), 0);
+        BOOST_CHECK_EQUAL(t.get_nsec (), 0);
+    }
+
+    BOOST_AUTO_TEST_CASE(initializer_now)
+    {
+        boost::optional<I2n::clock::Time> t = I2n::clock::now ();
+
+        BOOST_CHECK(t);
+        BOOST_CHECK_GT(t->get_sec (), 0);
+        BOOST_CHECK_EQUAL(t->err, 0);
+    }
+
+BOOST_AUTO_TEST_SUITE_END()
 
 BOOST_AUTO_TEST_SUITE_END()