Add first version
[ric-plt/sdl.git] / src / timerfd.cpp
diff --git a/src/timerfd.cpp b/src/timerfd.cpp
new file mode 100644 (file)
index 0000000..1356c50
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+   Copyright (c) 2018-2019 Nokia.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#include "private/timerfd.hpp"
+#include "private/engine.hpp"
+#include "private/system.hpp"
+
+using namespace shareddatalayer;
+
+TimerFD::TimerFD(Engine& engine):
+    TimerFD(System::getSystem(), engine)
+{
+}
+
+TimerFD::TimerFD(System& system, Engine& engine):
+    system(system),
+    fd(system, system.timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC))
+{
+    engine.addMonitoredFD(fd, Engine::EVENT_IN, std::bind(&TimerFD::handleEvents, this));
+}
+
+TimerFD::~TimerFD()
+{
+}
+
+void TimerFD::arm(Timer& timer, const Timer::Duration& duration, const Timer::Callback& cb)
+{
+    const auto absolute(toAbsolute(duration));
+    const auto i(queue.insert(std::make_pair(absolute, std::make_pair(&timer, cb))));
+    timer.iterator = i;
+    if (isFirst(i))
+        armTimerFD(absolute);
+}
+
+bool TimerFD::isFirst(Queue::iterator it) const
+{
+    return queue.begin() == it;
+}
+
+void TimerFD::disarm(const Timer& timer)
+{
+    const bool wasFirst(isFirst(timer.iterator));
+    queue.erase(timer.iterator);
+
+    if (queue.empty())
+        disarmTimerFD();
+    else if (wasFirst)
+        armTimerFD(nextTrigger());
+}
+
+Timer::Duration TimerFD::toAbsolute(const Timer::Duration& duration)
+{
+    return std::chrono::duration_cast<Timer::Duration>(system.time_since_epoch()) + duration;
+}
+
+void TimerFD::handleEvents()
+{
+    if (timerExpired())
+        handleExpiredTimers();
+}
+
+bool TimerFD::timerExpired() const
+{
+    uint64_t count;
+    return (system.read(fd, &count, sizeof(count)) == sizeof(count)) && (count > 0U);
+}
+
+void TimerFD::handleExpiredTimers()
+{
+    const auto now(system.time_since_epoch());
+    do
+    {
+        popAndExecuteFirstTimer();
+        if (queue.empty())
+        {
+            disarmTimerFD();
+            return;
+        }
+    }
+    while (queue.begin()->first <= now);
+    armTimerFD(nextTrigger());
+}
+
+void TimerFD::popAndExecuteFirstTimer()
+{
+    const auto i(queue.begin());
+    const auto cb(i->second.second);
+    queue.erase(i);
+    cb();
+}
+
+Timer::Duration TimerFD::nextTrigger() const
+{
+    return queue.begin()->first;
+}
+
+void TimerFD::disarmTimerFD()
+{
+    setTimeForTimerFD(0, 0);
+}
+
+void TimerFD::armTimerFD(const Timer::Duration& duration)
+{
+    static const long int NANOSECONDS_IN_ONE_SECOND(1E9);
+    setTimeForTimerFD(std::chrono::duration_cast<std::chrono::seconds>(duration).count(),
+                      std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % NANOSECONDS_IN_ONE_SECOND);
+}
+
+void TimerFD::setTimeForTimerFD(time_t seconds, long int nanoseconds)
+{
+    const itimerspec ts{ { 0, 0 }, { seconds, nanoseconds } };
+    system.timerfd_settime(fd, TFD_TIMER_ABSTIME, &ts, nullptr);
+}