--- /dev/null
+/*
+ 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);
+}