Add first version
[ric-plt/sdl.git] / src / timerfd.cpp
1 /*
2    Copyright (c) 2018-2019 Nokia.
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15 */
16
17 #include "private/timerfd.hpp"
18 #include "private/engine.hpp"
19 #include "private/system.hpp"
20
21 using namespace shareddatalayer;
22
23 TimerFD::TimerFD(Engine& engine):
24     TimerFD(System::getSystem(), engine)
25 {
26 }
27
28 TimerFD::TimerFD(System& system, Engine& engine):
29     system(system),
30     fd(system, system.timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC))
31 {
32     engine.addMonitoredFD(fd, Engine::EVENT_IN, std::bind(&TimerFD::handleEvents, this));
33 }
34
35 TimerFD::~TimerFD()
36 {
37 }
38
39 void TimerFD::arm(Timer& timer, const Timer::Duration& duration, const Timer::Callback& cb)
40 {
41     const auto absolute(toAbsolute(duration));
42     const auto i(queue.insert(std::make_pair(absolute, std::make_pair(&timer, cb))));
43     timer.iterator = i;
44     if (isFirst(i))
45         armTimerFD(absolute);
46 }
47
48 bool TimerFD::isFirst(Queue::iterator it) const
49 {
50     return queue.begin() == it;
51 }
52
53 void TimerFD::disarm(const Timer& timer)
54 {
55     const bool wasFirst(isFirst(timer.iterator));
56     queue.erase(timer.iterator);
57
58     if (queue.empty())
59         disarmTimerFD();
60     else if (wasFirst)
61         armTimerFD(nextTrigger());
62 }
63
64 Timer::Duration TimerFD::toAbsolute(const Timer::Duration& duration)
65 {
66     return std::chrono::duration_cast<Timer::Duration>(system.time_since_epoch()) + duration;
67 }
68
69 void TimerFD::handleEvents()
70 {
71     if (timerExpired())
72         handleExpiredTimers();
73 }
74
75 bool TimerFD::timerExpired() const
76 {
77     uint64_t count;
78     return (system.read(fd, &count, sizeof(count)) == sizeof(count)) && (count > 0U);
79 }
80
81 void TimerFD::handleExpiredTimers()
82 {
83     const auto now(system.time_since_epoch());
84     do
85     {
86         popAndExecuteFirstTimer();
87         if (queue.empty())
88         {
89             disarmTimerFD();
90             return;
91         }
92     }
93     while (queue.begin()->first <= now);
94     armTimerFD(nextTrigger());
95 }
96
97 void TimerFD::popAndExecuteFirstTimer()
98 {
99     const auto i(queue.begin());
100     const auto cb(i->second.second);
101     queue.erase(i);
102     cb();
103 }
104
105 Timer::Duration TimerFD::nextTrigger() const
106 {
107     return queue.begin()->first;
108 }
109
110 void TimerFD::disarmTimerFD()
111 {
112     setTimeForTimerFD(0, 0);
113 }
114
115 void TimerFD::armTimerFD(const Timer::Duration& duration)
116 {
117     static const long int NANOSECONDS_IN_ONE_SECOND(1E9);
118     setTimeForTimerFD(std::chrono::duration_cast<std::chrono::seconds>(duration).count(),
119                       std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % NANOSECONDS_IN_ONE_SECOND);
120 }
121
122 void TimerFD::setTimeForTimerFD(time_t seconds, long int nanoseconds)
123 {
124     const itimerspec ts{ { 0, 0 }, { seconds, nanoseconds } };
125     system.timerfd_settime(fd, TFD_TIMER_ABSTIME, &ts, nullptr);
126 }