/src/Fast-DDS/src/cpp/rtps/resources/ResourceEvent.cpp
Line | Count | Source |
1 | | // Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | /** |
16 | | * @file ResourceEvent.cpp |
17 | | */ |
18 | | |
19 | | #include <rtps/resources/ResourceEvent.h> |
20 | | |
21 | | #include <cassert> |
22 | | |
23 | | #include <fastdds/dds/log/Log.hpp> |
24 | | |
25 | | #include "TimedEventImpl.h" |
26 | | #include <utils/thread.hpp> |
27 | | #include <utils/threading.hpp> |
28 | | |
29 | | namespace eprosima { |
30 | | namespace fastdds { |
31 | | namespace rtps { |
32 | | |
33 | | static bool event_compare( |
34 | | TimedEventImpl* lhs, |
35 | | TimedEventImpl* rhs) |
36 | 0 | { |
37 | 0 | return lhs->next_trigger_time() < rhs->next_trigger_time(); |
38 | 0 | } |
39 | | |
40 | | ResourceEvent::ResourceEvent() |
41 | 0 | : thread_(new eprosima::thread()) |
42 | 0 | { |
43 | 0 | } |
44 | | |
45 | | ResourceEvent::~ResourceEvent() |
46 | 0 | { |
47 | | // All timer should be unregistered before destroying this object. |
48 | 0 | assert(pending_timers_.empty()); |
49 | 0 | assert(timers_count_ == 0); |
50 | |
|
51 | 0 | stop_thread(); |
52 | 0 | } |
53 | | |
54 | | void ResourceEvent::stop_thread() |
55 | 0 | { |
56 | 0 | EPROSIMA_LOG_INFO(RTPS_PARTICIPANT, "Removing event thread"); |
57 | 0 | if (thread_->joinable()) |
58 | 0 | { |
59 | 0 | { |
60 | 0 | std::lock_guard<TimedMutex> guard(mutex_); |
61 | 0 | stop_.store(true); |
62 | 0 | cv_.notify_one(); |
63 | 0 | } |
64 | 0 | thread_->join(); |
65 | 0 | } |
66 | 0 | } |
67 | | |
68 | | void ResourceEvent::register_timer( |
69 | | TimedEventImpl* /*event*/) |
70 | 0 | { |
71 | 0 | { |
72 | 0 | std::lock_guard<TimedMutex> lock(mutex_); |
73 | 0 | ++timers_count_; |
74 | 0 | } |
75 | | |
76 | | // Notify the execution thread that something changed |
77 | 0 | cv_.notify_one(); |
78 | 0 | } |
79 | | |
80 | | void ResourceEvent::unregister_timer( |
81 | | TimedEventImpl* event) |
82 | 0 | { |
83 | 0 | std::unique_lock<TimedMutex> lock(mutex_); |
84 | |
|
85 | 0 | bool is_service_thread = thread_->is_calling_thread(); |
86 | | |
87 | | //! Let the service thread to manipulate resources |
88 | 0 | if (!is_service_thread) |
89 | 0 | { |
90 | 0 | cv_manipulation_.wait(lock, [&]() |
91 | 0 | { |
92 | 0 | return allow_vector_manipulation_; |
93 | 0 | }); |
94 | 0 | } |
95 | |
|
96 | 0 | bool should_notify = false; |
97 | 0 | std::vector<TimedEventImpl*>::iterator it; |
98 | | |
99 | | // Remove from pending |
100 | 0 | it = std::find(pending_timers_.begin(), pending_timers_.end(), event); |
101 | 0 | if (it != pending_timers_.end()) |
102 | 0 | { |
103 | 0 | pending_timers_.erase(it); |
104 | 0 | should_notify = true; |
105 | 0 | } |
106 | | |
107 | | // Remove from active |
108 | 0 | it = std::find(active_timers_.begin(), active_timers_.end(), event); |
109 | 0 | if (it != active_timers_.end()) |
110 | 0 | { |
111 | 0 | active_timers_.erase(it); |
112 | |
|
113 | 0 | if (is_service_thread) |
114 | 0 | { |
115 | | //! Warn the do_timer_actions loop to skip checking the rest of active_timers |
116 | | //! in this iteration to prevent iterator invalidation |
117 | 0 | skip_checking_active_timers_.store(true); |
118 | 0 | } |
119 | |
|
120 | 0 | should_notify = true; |
121 | 0 | } |
122 | | |
123 | | // Decrement counter of created timers |
124 | 0 | --timers_count_; |
125 | |
|
126 | 0 | if (should_notify) |
127 | 0 | { |
128 | | // Notify the execution thread that something changed |
129 | 0 | cv_.notify_one(); |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | | void ResourceEvent::notify( |
134 | | TimedEventImpl* event) |
135 | 0 | { |
136 | 0 | std::lock_guard<TimedMutex> lock(mutex_); |
137 | |
|
138 | 0 | if (register_timer_nts(event)) |
139 | 0 | { |
140 | | // Notify the execution thread that something changed |
141 | 0 | cv_.notify_one(); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | void ResourceEvent::notify( |
146 | | TimedEventImpl* event, |
147 | | const std::chrono::steady_clock::time_point& timeout) |
148 | 0 | { |
149 | | #if HAVE_STRICT_REALTIME |
150 | | std::unique_lock<TimedMutex> lock(mutex_, std::defer_lock); |
151 | | if (lock.try_lock_until(timeout)) |
152 | | #else |
153 | 0 | static_cast<void>(timeout); |
154 | 0 | std::lock_guard<TimedMutex> _(mutex_); |
155 | 0 | #endif // HAVE_STRICT_REALTIME |
156 | 0 | { |
157 | 0 | if (register_timer_nts(event)) |
158 | 0 | { |
159 | | // Notify the execution thread that something changed |
160 | 0 | cv_.notify_one(); |
161 | 0 | } |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | bool ResourceEvent::register_timer_nts( |
166 | | TimedEventImpl* event) |
167 | 0 | { |
168 | 0 | if (std::find(pending_timers_.begin(), pending_timers_.end(), event) == pending_timers_.end()) |
169 | 0 | { |
170 | 0 | pending_timers_.push_back(event); |
171 | 0 | return true; |
172 | 0 | } |
173 | | |
174 | 0 | return false; |
175 | 0 | } |
176 | | |
177 | | void ResourceEvent::event_service() |
178 | 0 | { |
179 | 0 | while (!stop_.load()) |
180 | 0 | { |
181 | | // Perform update and execution of timers |
182 | 0 | update_current_time(); |
183 | 0 | do_timer_actions(); |
184 | |
|
185 | 0 | std::unique_lock<TimedMutex> lock(mutex_); |
186 | | |
187 | | // If the thread has already been instructed to stop, do it. |
188 | 0 | if (stop_.load()) |
189 | 0 | { |
190 | 0 | break; |
191 | 0 | } |
192 | | |
193 | | // If pending timers exist, there is some work to be done, so no need to wait. |
194 | 0 | if (!pending_timers_.empty()) |
195 | 0 | { |
196 | 0 | continue; |
197 | 0 | } |
198 | | |
199 | | // Allow other threads to manipulate the timer collections while we wait. |
200 | 0 | allow_vector_manipulation_ = true; |
201 | 0 | cv_manipulation_.notify_all(); |
202 | | |
203 | | // Wait for the first timer to be triggered |
204 | 0 | std::chrono::steady_clock::time_point next_trigger = |
205 | 0 | active_timers_.empty() ? |
206 | 0 | current_time_ + std::chrono::seconds(1) : |
207 | 0 | active_timers_[0]->next_trigger_time(); |
208 | |
|
209 | 0 | auto current_time = std::chrono::steady_clock::now(); |
210 | 0 | if (current_time > next_trigger) |
211 | 0 | { |
212 | 0 | next_trigger = current_time + std::chrono::microseconds(10); |
213 | 0 | } |
214 | |
|
215 | 0 | cv_.wait_until(lock, next_trigger); |
216 | | |
217 | | // Don't allow other threads to manipulate the timer collections |
218 | 0 | allow_vector_manipulation_ = false; |
219 | 0 | resize_collections(); |
220 | 0 | } |
221 | | |
222 | | // Thread being stopped. Allow other threads to manipulate the timer collections. |
223 | 0 | { |
224 | 0 | std::lock_guard<TimedMutex> guard(mutex_); |
225 | 0 | allow_vector_manipulation_ = true; |
226 | 0 | } |
227 | 0 | cv_manipulation_.notify_all(); |
228 | 0 | } |
229 | | |
230 | | void ResourceEvent::sort_timers() |
231 | 0 | { |
232 | 0 | std::sort(active_timers_.begin(), active_timers_.end(), event_compare); |
233 | 0 | } |
234 | | |
235 | | void ResourceEvent::update_current_time() |
236 | 0 | { |
237 | 0 | current_time_ = std::chrono::steady_clock::now(); |
238 | 0 | } |
239 | | |
240 | | void ResourceEvent::do_timer_actions() |
241 | 0 | { |
242 | 0 | std::chrono::steady_clock::time_point cancel_time = |
243 | 0 | current_time_ + std::chrono::hours(24); |
244 | |
|
245 | 0 | bool did_something = false; |
246 | | |
247 | | // Process pending orders |
248 | 0 | { |
249 | 0 | std::lock_guard<TimedMutex> lock(mutex_); |
250 | 0 | for (TimedEventImpl* tp : pending_timers_) |
251 | 0 | { |
252 | | // Remove item from active timers |
253 | 0 | auto current_pos = std::lower_bound(active_timers_.begin(), active_timers_.end(), tp, event_compare); |
254 | 0 | current_pos = std::find(current_pos, active_timers_.end(), tp); |
255 | 0 | if (current_pos != active_timers_.end()) |
256 | 0 | { |
257 | 0 | active_timers_.erase(current_pos); |
258 | 0 | } |
259 | | |
260 | | // Update timer info |
261 | 0 | if (tp->update(current_time_, cancel_time)) |
262 | 0 | { |
263 | | // Timer has to be activated: add to active timers |
264 | 0 | std::vector<TimedEventImpl*>::iterator low_bound; |
265 | | |
266 | | // Insert on correct position |
267 | 0 | low_bound = std::lower_bound(active_timers_.begin(), active_timers_.end(), tp, event_compare); |
268 | 0 | active_timers_.emplace(low_bound, tp); |
269 | 0 | } |
270 | 0 | } |
271 | 0 | pending_timers_.clear(); |
272 | 0 | } |
273 | | |
274 | | // Trigger active timers |
275 | 0 | skip_checking_active_timers_.store(false); |
276 | 0 | for (TimedEventImpl* tp : active_timers_) |
277 | 0 | { |
278 | 0 | if (tp->next_trigger_time() <= current_time_) |
279 | 0 | { |
280 | 0 | did_something = true; |
281 | 0 | tp->trigger(current_time_, cancel_time); |
282 | | |
283 | | //! skip this iteration as active_timers has been manipulated |
284 | 0 | if (skip_checking_active_timers_.load()) |
285 | 0 | { |
286 | 0 | break; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | else |
290 | 0 | { |
291 | 0 | break; |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | // If an action was made, keep active_timers_ sorted |
296 | 0 | if (did_something) |
297 | 0 | { |
298 | 0 | sort_timers(); |
299 | 0 | active_timers_.erase( |
300 | 0 | std::lower_bound(active_timers_.begin(), active_timers_.end(), nullptr, |
301 | 0 | [cancel_time]( |
302 | 0 | TimedEventImpl* a, |
303 | 0 | TimedEventImpl* b) |
304 | 0 | { |
305 | 0 | (void)b; |
306 | 0 | return a->next_trigger_time() < cancel_time; |
307 | 0 | }), |
308 | 0 | active_timers_.end() |
309 | 0 | ); |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | | void ResourceEvent::init_thread( |
314 | | const fastdds::rtps::ThreadSettings& thread_cfg, |
315 | | const char* name_fmt, |
316 | | uint32_t thread_id) |
317 | 0 | { |
318 | 0 | std::lock_guard<TimedMutex> lock(mutex_); |
319 | |
|
320 | 0 | allow_vector_manipulation_ = false; |
321 | 0 | stop_.store(false); |
322 | 0 | resize_collections(); |
323 | |
|
324 | 0 | *thread_ = eprosima::create_thread([this]() |
325 | 0 | { |
326 | 0 | event_service(); |
327 | 0 | }, thread_cfg, name_fmt, thread_id); |
328 | 0 | } |
329 | | |
330 | | } /* namespace rtps */ |
331 | | } /* namespace fastdds */ |
332 | | } /* namespace eprosima */ |