Merge branch '1.3-rel' (5fdb8a1)
[iotivity.git] / service / resource-encapsulation / src / common / expiryTimer / src / ExpiryTimerImpl.cpp
1 //******************************************************************
2 //
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 #include "ExpiryTimerImpl.h"
22
23 #include "RCSException.h"
24
25 namespace OIC
26 {
27     namespace Service
28     {
29
30         namespace
31         {
32             constexpr ExpiryTimerImpl::Id INVALID_ID{ 0U };
33         }
34
35         ExpiryTimerImpl::ExpiryTimerImpl() :
36                 m_tasks{ },
37                 m_thread{ },
38                 m_mutex{ },
39                 m_cond{ },
40                 m_stop{ false },
41                 m_mt{ std::random_device{ }() },
42                 m_dist{ }
43         {
44             m_thread = std::thread(&ExpiryTimerImpl::run, this);
45         }
46
47         ExpiryTimerImpl::~ExpiryTimerImpl()
48         {
49             {
50                 std::lock_guard< std::mutex > lock{ m_mutex };
51                 m_tasks.clear();
52                 m_stop = true;
53             }
54             m_cond.notify_all();
55             m_thread.join();
56         }
57
58         ExpiryTimerImpl* ExpiryTimerImpl::getInstance()
59         {
60             static ExpiryTimerImpl instance;
61             return &instance;
62         }
63
64         std::shared_ptr< TimerTask > ExpiryTimerImpl::post(DelayInMillis delay, Callback cb)
65         {
66             if (delay < 0LL)
67             {
68                 throw RCSInvalidParameterException{ "delay can't be negative." };
69             }
70
71             if (!cb)
72             {
73                 throw RCSInvalidParameterException{ "callback is empty." };
74             }
75
76             return addTask(convertToTime(Milliseconds{ delay }), std::move(cb), generateId());
77         }
78
79         bool ExpiryTimerImpl::cancel(Id id)
80         {
81             if (id == INVALID_ID)
82             {
83                 return false;
84             }
85
86             std::lock_guard< std::mutex > lock{ m_mutex };
87
88             for(auto it = m_tasks.begin(); it != m_tasks.end(); ++it)
89             {
90                 if(it->second->getId() == id)
91                 {
92                     m_tasks.erase(it);
93                     return true;
94                 }
95             }
96             return false;
97         }
98
99         size_t ExpiryTimerImpl::cancelAll(
100                 const std::unordered_set< std::shared_ptr<TimerTask > >& tasks)
101         {
102             std::lock_guard< std::mutex > lock{ m_mutex };
103             size_t erased { 0 };
104
105             for(auto it = m_tasks.begin(); it != m_tasks.end();)
106             {
107                 if(tasks.count(it->second))
108                 {
109                     it = m_tasks.erase(it);
110                     ++erased;
111                 }
112                 else
113                 {
114                     ++it;
115                 }
116             }
117             return erased;
118         }
119
120         ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::convertToTime(Milliseconds delay)
121         {
122             const auto now = std::chrono::system_clock::now();
123             return std::chrono::duration_cast< Milliseconds >(now.time_since_epoch()) + delay;
124         }
125
126         std::shared_ptr< TimerTask > ExpiryTimerImpl::addTask(
127                 Milliseconds delay, Callback cb, Id id)
128         {
129             std::lock_guard< std::mutex > lock{ m_mutex };
130
131             auto newTask = std::make_shared< TimerTask >(id, std::move(cb));
132             m_tasks.insert({ delay, newTask });
133             m_cond.notify_all();
134
135             return newTask;
136         }
137
138         bool ExpiryTimerImpl::containsId(Id id) const
139         {
140             for (const auto& info : m_tasks)
141             {
142                 if (info.second->getId() == id)
143                 {
144                     return true;
145                 }
146             }
147             return false;
148         }
149
150         ExpiryTimerImpl::Id ExpiryTimerImpl::generateId()
151         {
152             Id newId = m_dist(m_mt);
153
154             std::lock_guard< std::mutex > lock{ m_mutex };
155
156             while (newId == INVALID_ID || containsId(newId))
157             {
158                 newId = m_dist(m_mt);
159             }
160             return newId;
161         }
162
163         void ExpiryTimerImpl::executeExpired()
164         {
165             if (m_tasks.empty())
166             {
167                 return;
168             }
169
170             auto now = std::chrono::system_clock::now().time_since_epoch();
171
172             auto it = m_tasks.begin();
173             for (; it != m_tasks.end() && it->first <= now; ++it)
174             {
175                 it->second->execute();
176             }
177
178             m_tasks.erase(m_tasks.begin(), it);
179         }
180
181         ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::remainingTimeForNext() const
182         {
183             const Milliseconds& expiredTime = m_tasks.begin()->first;
184
185             return std::chrono::duration_cast< Milliseconds >(expiredTime -
186                     std::chrono::system_clock::now().time_since_epoch()) + Milliseconds{ 1 };
187         }
188
189         void ExpiryTimerImpl::run()
190         {
191             auto hasTaskOrStop = [this]()
192             {
193                 return !m_tasks.empty() || m_stop;
194             };
195
196             std::unique_lock< std::mutex > lock{ m_mutex };
197
198             while(!m_stop)
199             {
200                 m_cond.wait(lock, hasTaskOrStop);
201
202                 if (m_stop)
203                 {
204                     break;
205                 }
206
207                 m_cond.wait_for(lock, remainingTimeForNext());
208
209                 executeExpired();
210             }
211         }
212
213
214         TimerTask::TimerTask(ExpiryTimerImpl::Id id, ExpiryTimerImpl::Callback cb) :
215             m_id{ id },
216             m_callback{ std::move(cb) }
217         {
218         }
219
220         void TimerTask::execute()
221         {
222             if (isExecuted())
223             {
224                 return;
225             }
226
227             ExpiryTimerImpl::Id id { m_id };
228             m_id = INVALID_ID;
229
230             std::thread(std::move(m_callback), id).detach();
231
232             m_callback = ExpiryTimerImpl::Callback{ };
233         }
234
235         bool TimerTask::isExecuted() const
236         {
237             return m_id == INVALID_ID;
238         }
239
240         ExpiryTimerImpl::Id TimerTask::getId() const
241         {
242             return m_id;
243         }
244
245     }
246 }