Imported Upstream version 0.9.2
[platform/upstream/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{ std::thread(&ExpiryTimerImpl::run, this) },
38                 m_mutex{ },
39                 m_cond{ },
40                 m_stop{ false },
41                 m_mt{ std::random_device{ }() },
42                 m_dist{ }
43         {
44         }
45
46         ExpiryTimerImpl::~ExpiryTimerImpl()
47         {
48             {
49                 std::lock_guard< std::mutex > lock{ m_mutex };
50                 m_tasks.clear();
51                 m_stop = true;
52             }
53             m_cond.notify_all();
54             m_thread.join();
55         }
56
57         ExpiryTimerImpl* ExpiryTimerImpl::getInstance()
58         {
59             static ExpiryTimerImpl instance;
60             return &instance;
61         }
62
63         std::shared_ptr< TimerTask > ExpiryTimerImpl::post(DelayInMillis delay, Callback cb)
64         {
65             if (delay < 0LL)
66             {
67                 throw InvalidParameterException{ "delay can't be negative." };
68             }
69
70             if (!cb)
71             {
72                 throw InvalidParameterException{ "callback is empty." };
73             }
74
75             return addTask(convertToTime(Milliseconds{ delay }), std::move(cb), generateId());
76         }
77
78         bool ExpiryTimerImpl::cancel(Id id)
79         {
80             if (id == INVALID_ID) return false;
81
82             std::lock_guard< std::mutex > lock{ m_mutex };
83
84             for(auto it = m_tasks.begin(); it != m_tasks.end(); ++it)
85             {
86                 if(it->second->getId() == id)
87                 {
88                     m_tasks.erase(it);
89                     return true;
90                 }
91             }
92             return false;
93         }
94
95         size_t ExpiryTimerImpl::cancelAll(
96                 const std::unordered_set< std::shared_ptr<TimerTask > >& tasks)
97         {
98             std::lock_guard< std::mutex > lock{ m_mutex };
99             size_t erased { 0 };
100
101             for(auto it = m_tasks.begin(); it != m_tasks.end();)
102             {
103                 if(tasks.count(it->second))
104                 {
105                     it = m_tasks.erase(it);
106                     ++erased;
107                 }
108                 else
109                 {
110                     ++it;
111                 }
112             }
113             return erased;
114         }
115
116         ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::convertToTime(Milliseconds delay)
117         {
118             const auto now = std::chrono::system_clock::now();
119             return std::chrono::duration_cast< Milliseconds >(now.time_since_epoch()) + delay;
120         }
121
122         std::shared_ptr< TimerTask > ExpiryTimerImpl::addTask(
123                 Milliseconds delay, Callback cb, Id id)
124         {
125             std::lock_guard< std::mutex > lock{ m_mutex };
126
127             auto newTask = std::make_shared< TimerTask >(id, std::move(cb));
128             m_tasks.insert({ delay, newTask });
129             m_cond.notify_all();
130
131             return newTask;
132         }
133
134         bool ExpiryTimerImpl::containsId(Id id) const
135         {
136             for (const auto& info : m_tasks)
137             {
138                 if (info.second->getId() == id) return true;
139             }
140             return false;
141         }
142
143         ExpiryTimerImpl::Id ExpiryTimerImpl::generateId()
144         {
145             Id newId = m_dist(m_mt);
146
147             std::lock_guard< std::mutex > lock{ m_mutex };
148
149             while (newId == INVALID_ID || containsId(newId))
150             {
151                 newId = m_dist(m_mt);
152             }
153             return newId;
154         }
155
156         void ExpiryTimerImpl::executeExpired()
157         {
158             if (m_tasks.empty()) return;
159
160             auto now = std::chrono::system_clock::now().time_since_epoch();
161
162             auto it = m_tasks.begin();
163             for (; it != m_tasks.end() && it->first <= now; ++it)
164             {
165                 it->second->execute();
166             }
167
168             m_tasks.erase(m_tasks.begin(), it);
169         }
170
171         ExpiryTimerImpl::Milliseconds ExpiryTimerImpl::remainingTimeForNext() const
172         {
173             const Milliseconds& expiredTime = m_tasks.begin()->first;
174
175             return std::chrono::duration_cast< Milliseconds >(expiredTime -
176                     std::chrono::system_clock::now().time_since_epoch()) + Milliseconds{ 1 };
177         }
178
179         void ExpiryTimerImpl::run()
180         {
181             auto hasTaskOrStop = [this](){ return !m_tasks.empty() || m_stop; };
182
183             std::unique_lock< std::mutex > lock{ m_mutex };
184
185             while(!m_stop)
186             {
187                 m_cond.wait(lock, hasTaskOrStop);
188
189                 if (m_stop) break;
190
191                 m_cond.wait_for(lock, remainingTimeForNext());
192
193                 executeExpired();
194             }
195         }
196
197
198         TimerTask::TimerTask(ExpiryTimerImpl::Id id, ExpiryTimerImpl::Callback cb) :
199             m_id{ id },
200             m_callback{ std::move(cb) }
201         {
202         }
203
204         void TimerTask::execute()
205         {
206             if (isExecuted()) return;
207
208             ExpiryTimerImpl::Id id { m_id };
209             m_id = INVALID_ID;
210
211             std::thread(std::move(m_callback), id).detach();
212
213             m_callback = ExpiryTimerImpl::Callback{ };
214         }
215
216         bool TimerTask::isExecuted() const
217         {
218             return m_id == INVALID_ID;
219         }
220
221         ExpiryTimerImpl::Id TimerTask::getId() const
222         {
223             return m_id;
224         }
225
226     }
227 }