Coverage Report

Created: 2026-10-28 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */