Skip to content

Commit ce4904f

Browse files
jparisursanchez15
andauthored
Slot ThreadPool (#241)
* Implement ThreadPool in utils Signed-off-by: jparisu <javierparis@eprosima.com> * fix tests Signed-off-by: jparisu <javierparis@eprosima.com> * add include needed Signed-off-by: jparisu <javierparis@eprosima.com> * add dependency for test in windows Signed-off-by: jparisu <javierparis@eprosima.com> * add forthcoming version Signed-off-by: jparisu <javierparis@eprosima.com> * Add thread to yaml configuration Signed-off-by: jparisu <javierparis@eprosima.com> * Add in-code documentation Signed-off-by: jparisu <javierparis@eprosima.com> * Fix windows build adding missing DLLs Signed-off-by: Raúl Sánchez-Mateos <raul@eprosima.com> * Eliminate not using ThreadPool Signed-off-by: jparisu <javierparis@eprosima.com> * add enable/disable methods to SlotThreadPool Signed-off-by: jparisu <javierparis@eprosima.com> * uncrustify Signed-off-by: jparisu <javierparis@eprosima.com> * add dll for windows to enable disable Signed-off-by: jparisu <javierparis@eprosima.com> * apply docu suggestions Signed-off-by: jparisu <javierparis@eprosima.com> Co-authored-by: Raúl Sánchez-Mateos <raul@eprosima.com>
1 parent ca1428a commit ce4904f

26 files changed

Lines changed: 954 additions & 105 deletions

File tree

ddsrouter_core/include/ddsrouter_core/configuration/DDSRouterConfiguration.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class DDSRouterConfiguration : public DDSRouterReloadConfiguration
5050
std::set<std::shared_ptr<types::FilterTopic>> allowlist,
5151
std::set<std::shared_ptr<types::FilterTopic>> blocklist,
5252
std::set<std::shared_ptr<types::RealTopic>> builtin_topics,
53-
std::set<std::shared_ptr<ParticipantConfiguration>> participants_configurations);
53+
std::set<std::shared_ptr<ParticipantConfiguration>> participants_configurations,
54+
unsigned int number_of_threads = default_number_of_threads());
5455

5556
/**
5657
* @brief Return a set with the different \c ParticipantConfigurations in the yaml
@@ -68,12 +69,20 @@ class DDSRouterConfiguration : public DDSRouterReloadConfiguration
6869
DDSROUTER_CORE_DllAPI void reload(
6970
const DDSRouterReloadConfiguration& new_configuration);
7071

72+
DDSROUTER_CORE_DllAPI unsigned int number_of_threads() const noexcept;
73+
74+
DDSROUTER_CORE_DllAPI static unsigned int default_number_of_threads() noexcept;
75+
7176
protected:
7277

7378
static bool check_correct_configuration_object_(
7479
const std::shared_ptr<ParticipantConfiguration> configuration);
7580

7681
std::set<std::shared_ptr<ParticipantConfiguration>> participants_configurations_;
82+
83+
unsigned int number_of_threads_;
84+
85+
static const unsigned int DEFAULT_NUMBER_OF_THREADS_;
7786
};
7887

7988
} /* namespace configuration */

ddsrouter_core/src/cpp/communication/Bridge.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Bridge::Bridge(
3131
const RealTopic& topic,
3232
std::shared_ptr<ParticipantsDatabase> participants_database,
3333
std::shared_ptr<PayloadPool> payload_pool,
34+
std::shared_ptr<utils::SlotThreadPool> thread_pool,
3435
bool enable /* = false */)
3536
: topic_(topic)
3637
, participants_(participants_database)
@@ -62,7 +63,13 @@ Bridge::Bridge(
6263
// This insert is required as there is no copy method for Track
6364
// Tracks are always created disabled and then enabled with Bridge enable() method
6465
tracks_[id] =
65-
std::make_unique<Track>(topic_, id, readers_[id], std::move(writers_except_one), payload_pool_, false);
66+
std::make_unique<Track>(
67+
topic_,
68+
id,
69+
readers_[id], std::move(writers_except_one),
70+
payload_pool_,
71+
thread_pool,
72+
false);
6673
}
6774

6875
if (enable)

ddsrouter_core/src/cpp/communication/Bridge.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <participant/IParticipant.hpp>
2626
#include <core/ParticipantsDatabase.hpp>
2727
#include <ddsrouter_core/types/participant/ParticipantId.hpp>
28+
#include <ddsrouter_utils/thread_pool/pool/SlotThreadPool.hpp>
2829

2930
namespace eprosima {
3031
namespace ddsrouter {
@@ -58,6 +59,7 @@ class Bridge
5859
const types::RealTopic& topic,
5960
std::shared_ptr<ParticipantsDatabase> participants_database,
6061
std::shared_ptr<PayloadPool> payload_pool,
62+
std::shared_ptr<utils::SlotThreadPool> thread_pool,
6163
bool enable = false);
6264

6365
/**

ddsrouter_core/src/cpp/communication/Track.cpp

Lines changed: 49 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include <ddsrouter_utils/exception/UnsupportedException.hpp>
2121
#include <ddsrouter_utils/Log.hpp>
22+
#include <ddsrouter_utils/thread_pool/pool/SlotThreadPool.hpp>
23+
#include <ddsrouter_utils/thread_pool/task/TaskId.hpp>
2224

2325
#include <communication/Track.hpp>
2426

@@ -28,12 +30,15 @@ namespace core {
2830

2931
using namespace eprosima::ddsrouter::core::types;
3032

33+
const unsigned int Track::MAX_MESSAGES_TRANSMIT_LOOP_ = 100;
34+
3135
Track::Track(
3236
const RealTopic& topic,
3337
ParticipantId reader_participant_id,
3438
std::shared_ptr<IReader> reader,
3539
std::map<ParticipantId, std::shared_ptr<IWriter>>&& writers,
3640
std::shared_ptr<PayloadPool> payload_pool,
41+
std::shared_ptr<utils::SlotThreadPool> thread_pool,
3742
bool enable /* = false */) noexcept
3843
: reader_participant_id_(reader_participant_id)
3944
, topic_(topic)
@@ -43,14 +48,18 @@ Track::Track(
4348
, enabled_(false)
4449
, exit_(false)
4550
, data_available_status_(DataAvailableStatus::no_more_data)
51+
, thread_pool_(thread_pool)
52+
, transmit_task_id_(utils::new_unique_task_id())
4653
{
4754
logDebug(DDSROUTER_TRACK, "Creating Track " << *this << ".");
4855

4956
// Set this track to on_data_available lambda call
5057
reader_->set_on_data_available_callback(std::bind(&Track::data_available_, this));
5158

52-
// Activate transmit thread even without being enabled
53-
transmit_thread_ = std::thread(&Track::transmit_thread_function_, this);
59+
// Set slot in thread pool
60+
thread_pool_->slot(
61+
transmit_task_id_,
62+
std::bind(&Track::transmit_, this));
5463

5564
if (enable)
5665
{
@@ -71,21 +80,15 @@ Track::~Track()
7180
reader_->unset_on_data_available_callback();
7281

7382
// It does need to guard the mutex to avoid notifying Track thread while it is checking variable condition
74-
{
75-
// Set exit status and call transmit thread to awake and terminate. Then wait for it.
76-
std::lock_guard<std::mutex> lock(data_available_mutex_);
77-
exit_.store(true);
78-
}
79-
80-
data_available_condition_variable_.notify_all();
81-
transmit_thread_.join();
83+
// Set exit status and call transmit thread to awake and terminate. Then wait for it.
84+
exit_.store(true);
8285

8386
logDebug(DDSROUTER_TRACK, "Track " << *this << " destroyed.");
8487
}
8588

8689
void Track::enable() noexcept
8790
{
88-
std::lock_guard<std::recursive_mutex> lock(track_mutex_);
91+
std::lock_guard<std::mutex> lock(track_mutex_);
8992

9093
if (!enabled_)
9194
{
@@ -106,7 +109,7 @@ void Track::enable() noexcept
106109

107110
void Track::disable() noexcept
108111
{
109-
std::lock_guard<std::recursive_mutex> lock(track_mutex_);
112+
std::lock_guard<std::mutex> lock(track_mutex_);
110113

111114
if (enabled_)
112115
{
@@ -130,23 +133,6 @@ void Track::disable() noexcept
130133
}
131134
}
132135

133-
void Track::no_more_data_available_() noexcept
134-
{
135-
std::lock_guard<std::mutex> lock(data_available_mutex_);
136-
137-
// It may occur that within the process of set data_available_status, the actual status had changed
138-
// Thus, it must take care that it is only set to no_data when it comes from transmitting data
139-
if (data_available_status_ == DataAvailableStatus::transmitting_data)
140-
{
141-
logDebug(DDSROUTER_TRACK, "Track " << *this << " has no more data to send.");
142-
data_available_status_.store(DataAvailableStatus::no_more_data);
143-
}
144-
// If it is new_data_arrived is that the Listener has notified new data AFTER Track has received a no_data
145-
// from the Reader. Very unlikely timing, but possible.
146-
// In this occasion, it must not be set as no_more_data because THERE IS data.
147-
// If it is no_more_data it does not need to be changed (however it should never happen)
148-
}
149-
150136
bool Track::should_transmit_() noexcept
151137
{
152138
return !exit_ && enabled_ && this->is_data_available_();
@@ -159,14 +145,18 @@ void Track::data_available_() noexcept
159145
{
160146
logDebug(DDSROUTER_TRACK, "Track " << *this << " has data ready to be sent.");
161147

162-
// It does need to guard the mutex to avoid notifying Track thread while it is checking variable condition
148+
// Lock data_available_status_mutex_ to avoid changing the status while it is being checked
149+
std::lock_guard<std::mutex> lock(data_available_status_mutex_);
150+
151+
// This method will always be called from the Reader thread, so it is safe to set the status
152+
DataAvailableStatus current_status = data_available_status_.exchange(DataAvailableStatus::new_data_arrived);
153+
154+
if (current_status == DataAvailableStatus::no_more_data)
163155
{
164-
// Set data available to true and notify transmit thread
165-
std::lock_guard<std::mutex> lock(data_available_mutex_);
166-
data_available_status_.store(DataAvailableStatus::new_data_arrived);
156+
// Only send the callback to thread pool if it was not running
157+
thread_pool_->emit(transmit_task_id_);
158+
logDebug(DDSROUTER_TRACK, "Track " << *this << " send callback to queue.");
167159
}
168-
169-
data_available_condition_variable_.notify_one();
170160
}
171161
}
172162

@@ -176,45 +166,19 @@ bool Track::is_data_available_() const noexcept
176166
data_available_status_ == DataAvailableStatus::transmitting_data;
177167
}
178168

179-
void Track::transmit_thread_function_() noexcept
180-
{
181-
while (!exit_)
182-
{
183-
// Wait in Condition Variable till there is data to send or it must exit
184-
{
185-
std::unique_lock<std::mutex> lock(data_available_mutex_);
186-
data_available_condition_variable_.wait(
187-
lock,
188-
[this]
189-
{
190-
return this->is_data_available_() || this->exit_;
191-
});
192-
}
193-
194-
// Once thread awakes, transmit without any mutex guarded
195-
if (!this->exit_)
196-
{
197-
transmit_();
198-
}
199-
}
200-
}
201-
202169
void Track::transmit_() noexcept
203170
{
204171
// Loop that ends if it should stop transmitting (should_transmit_nts_).
205172
// Called inside the loop so it is protected by a mutex that is freed in every iteration.
206-
while (true)
207-
{
208-
// Lock Mutex on_transmition while a data is being transmitted
209-
// This prevents the Track to be disabled (and disable writers and readers) while sending a data
210-
std::unique_lock<std::mutex> lock(on_transmission_mutex_);
211173

212-
// If it must not keep transmitting, stop loop
213-
if (!should_transmit_())
214-
{
215-
break;
216-
}
174+
// Lock Mutex on_transmition while a data is being transmitted
175+
// This prevents the Track to be disabled (and disable writers and readers) while sending a data
176+
// enabled_ will be set to false before taking the mutex, so the track will finish after current iteration
177+
std::unique_lock<std::mutex> lock(on_transmission_mutex_);
217178

179+
// TODO: Count the times it loops to break it at some point if needed
180+
while (should_transmit_())
181+
{
218182
// It starts transmitting, so it sets the data available status as transmitting
219183
data_available_status_ = DataAvailableStatus::transmitting_data;
220184

@@ -224,16 +188,22 @@ void Track::transmit_() noexcept
224188

225189
if (ret == utils::ReturnCode::RETCODE_NO_DATA)
226190
{
191+
// Lock data_available_status_mutex_ to avoid changing the status while it is being checked
192+
std::lock_guard<std::mutex> lock(data_available_status_mutex_);
193+
227194
// There is no more data, so finish loop and wait again for new data
228-
no_more_data_available_();
229-
break;
230-
}
231-
else if (ret == utils::ReturnCode::RETCODE_NOT_ENABLED)
232-
{
233-
// This may not happen because the Reader is only disabled from here, however
234-
// it is better to cut it and set as no more data is available.
235-
no_more_data_available_();
236-
break;
195+
DataAvailableStatus current_status = data_available_status_.exchange(DataAvailableStatus::no_more_data);
196+
if (current_status == DataAvailableStatus::new_data_arrived)
197+
{
198+
// New data has arrived while setting no_more_data, so it should continues
199+
data_available_status_.store(DataAvailableStatus::transmitting_data);
200+
continue;
201+
}
202+
else
203+
{
204+
// no_more_data has been set, so if any other data arrives it will send a callback to thread pool
205+
break;
206+
}
237207
}
238208
else if (!ret)
239209
{
@@ -263,6 +233,8 @@ void Track::transmit_() noexcept
263233

264234
payload_pool_->release_payload(data->payload);
265235
}
236+
237+
data_available_status_.store(DataAvailableStatus::no_more_data);
266238
}
267239

268240
std::ostream& operator <<(

ddsrouter_core/src/cpp/communication/Track.hpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <participant/IParticipant.hpp>
2828
#include <reader/IReader.hpp>
2929
#include <writer/IWriter.hpp>
30+
#include <ddsrouter_utils/thread_pool/pool/SlotThreadPool.hpp>
3031

3132
namespace eprosima {
3233
namespace ddsrouter {
@@ -56,6 +57,7 @@ class Track
5657
std::shared_ptr<IReader> reader,
5758
std::map<types::ParticipantId, std::shared_ptr<IWriter>>&& writers,
5859
std::shared_ptr<PayloadPool> payload_pool,
60+
std::shared_ptr<utils::SlotThreadPool> thread_pool,
5961
bool enable = false) noexcept;
6062

6163
/**
@@ -205,7 +207,7 @@ class Track
205207
* Mutex to prevent simultaneous calls to \c enable and/or \c disable .
206208
* It manages access to variable \c enabled_ .
207209
*/
208-
std::recursive_mutex track_mutex_;
210+
std::mutex track_mutex_;
209211

210212
/////
211213
// Transmit thread part
@@ -232,27 +234,19 @@ class Track
232234
*/
233235
std::atomic<DataAvailableStatus> data_available_status_;
234236

235-
/**
236-
* Condition variable to wait for new data available or track termination.
237-
*/
238-
std::condition_variable data_available_condition_variable_;
239-
240-
/**
241-
* Mutex to handle access to condition variable \c data_available_condition_variable_ .
242-
* Mutex to manage access to variable \c data_available_status_ .
243-
*/
244-
std::mutex data_available_mutex_;
245-
246-
/**
247-
* Thread that will manage the transmission of the data
248-
*/
249-
std::thread transmit_thread_;
237+
std::mutex data_available_status_mutex_;
250238

251239
/**
252240
* Mutex to guard while the Track is sending a message.
253241
*/
254242
std::mutex on_transmission_mutex_;
255243

244+
utils::TaskId transmit_task_id_;
245+
246+
std::shared_ptr<utils::SlotThreadPool> thread_pool_;
247+
248+
static const unsigned int MAX_MESSAGES_TRANSMIT_LOOP_;
249+
256250
// Allow operator << to use private variables
257251
friend std::ostream& operator <<(
258252
std::ostream&,

ddsrouter_core/src/cpp/configuration/DDSRouterConfiguration.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ namespace configuration {
3333

3434
using namespace eprosima::ddsrouter::core::types;
3535

36+
const unsigned int DDSRouterConfiguration::DEFAULT_NUMBER_OF_THREADS_ = 12;
37+
3638
DDSRouterConfiguration::DDSRouterConfiguration(
3739
std::set<std::shared_ptr<FilterTopic>> allowlist,
3840
std::set<std::shared_ptr<FilterTopic>> blocklist,
3941
std::set<std::shared_ptr<RealTopic>> builtin_topics,
40-
std::set<std::shared_ptr<ParticipantConfiguration>> participants_configurations)
42+
std::set<std::shared_ptr<ParticipantConfiguration>> participants_configurations,
43+
unsigned int number_of_threads /* = default_number_of_threads() */)
4144
: DDSRouterReloadConfiguration (allowlist, blocklist, builtin_topics)
4245
, participants_configurations_(participants_configurations)
46+
, number_of_threads_(number_of_threads)
4347
{
4448
}
4549

@@ -137,6 +141,16 @@ bool DDSRouterConfiguration::check_correct_configuration_object_(
137141
}
138142
}
139143

144+
unsigned int DDSRouterConfiguration::number_of_threads() const noexcept
145+
{
146+
return number_of_threads_;
147+
}
148+
149+
unsigned int DDSRouterConfiguration::default_number_of_threads() noexcept
150+
{
151+
return DEFAULT_NUMBER_OF_THREADS_;
152+
}
153+
140154
} /* namespace configuration */
141155
} /* namespace core */
142156
} /* namespace ddsrouter */

0 commit comments

Comments
 (0)