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
2931using namespace eprosima ::ddsrouter::core::types;
3032
33+ const unsigned int Track::MAX_MESSAGES_TRANSMIT_LOOP_ = 100 ;
34+
3135Track::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
8689void 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
107110void 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-
150136bool 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-
202169void 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
268240std::ostream& operator <<(
0 commit comments