Skip to content

Commit 600102e

Browse files
authored
Merge pull request #320 from rock-core/fix_event_port_and_fda
fix default event port behavior along with FileDescriptorActivity
2 parents b1f367d + 67bd595 commit 600102e

File tree

3 files changed

+126
-41
lines changed

3 files changed

+126
-41
lines changed

rtt/extras/FileDescriptorActivity.cpp

+60-30
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ FileDescriptorActivity::FileDescriptorActivity(int priority, RunnableInterface*
8484
, m_has_timeout(false)
8585
, m_break_loop(false)
8686
, m_trigger(false)
87+
, m_user_timeout(false)
8788
, m_update_sets(false)
8889
{
8990
FD_ZERO(&m_fd_set);
@@ -109,6 +110,7 @@ FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, Runn
109110
, m_has_timeout(false)
110111
, m_break_loop(false)
111112
, m_trigger(false)
113+
, m_user_timeout(false)
112114
, m_update_sets(false)
113115
{
114116
FD_ZERO(&m_fd_set);
@@ -125,6 +127,7 @@ FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, Seco
125127
, m_has_timeout(false)
126128
, m_break_loop(false)
127129
, m_trigger(false)
130+
, m_user_timeout(false)
128131
, m_update_sets(false)
129132
{
130133
FD_ZERO(&m_fd_set);
@@ -141,6 +144,7 @@ FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, Seco
141144
, m_has_timeout(false)
142145
, m_break_loop(false)
143146
, m_trigger(false)
147+
, m_user_timeout(false)
144148
, m_update_sets(false)
145149
{
146150
FD_ZERO(&m_fd_set);
@@ -214,8 +218,7 @@ void FileDescriptorActivity::triggerUpdateSets()
214218
{ RTT::os::MutexLock lock(m_command_mutex);
215219
m_update_sets = true;
216220
}
217-
int unused; (void)unused;
218-
unused = write(m_interrupt_pipe[1], &CMD_ANY_COMMAND, 1);
221+
writeInterruptPipe();
219222
}
220223
bool FileDescriptorActivity::isUpdated(int fd) const
221224
{ return FD_ISSET(fd, &m_fd_work); }
@@ -257,6 +260,7 @@ bool FileDescriptorActivity::start()
257260
// reset flags
258261
m_break_loop = false;
259262
m_trigger = false;
263+
m_user_timeout = false;
260264
m_update_sets = false;
261265

262266
if (!Activity::start())
@@ -271,21 +275,27 @@ bool FileDescriptorActivity::start()
271275
}
272276

273277
bool FileDescriptorActivity::trigger()
274-
{
275-
if (isActive() ) {
278+
{
279+
if (isActive()) {
276280
{ RTT::os::MutexLock lock(m_command_mutex);
277281
m_trigger = true;
278282
}
279-
int unused; (void)unused;
280-
unused = write(m_interrupt_pipe[1], &CMD_ANY_COMMAND, 1);
283+
writeInterruptPipe();
281284
return true;
282285
} else
283286
return false;
284287
}
285288

286289
bool FileDescriptorActivity::timeout()
287290
{
288-
return false;
291+
if (isActive()) {
292+
{ RTT::os::MutexLock lock(m_command_mutex);
293+
m_user_timeout = true;
294+
}
295+
writeInterruptPipe();
296+
return true;
297+
} else
298+
return false;
289299
}
290300

291301

@@ -294,7 +304,7 @@ struct fd_watch {
294304
fd_watch(int& fd) : fd(fd) {}
295305
~fd_watch()
296306
{
297-
if (fd != -1)
307+
if (fd != -1)
298308
close(fd);
299309
fd = -1;
300310
}
@@ -327,17 +337,18 @@ void FileDescriptorActivity::loop()
327337
}
328338
else
329339
{
330-
static const int USECS_PER_SEC = 1000000;
340+
static const int USECS_PER_SEC = 1000000;
331341
timeval timeout = { m_timeout_us / USECS_PER_SEC,
332-
m_timeout_us % USECS_PER_SEC};
342+
m_timeout_us % USECS_PER_SEC };
333343
ret = select(max_fd + 1, &m_fd_work, NULL, NULL, &timeout);
334344
}
335345

336346
m_has_error = false;
337347
m_has_timeout = false;
338348
if (ret == -1)
339349
{
340-
log(Error) << "FileDescriptorActivity: error in select(), errno = " << errno << endlog();
350+
log(Error) << "FileDescriptorActivity: error in select(), errno = "
351+
<< errno << endlog();
341352
m_has_error = true;
342353
}
343354
else if (ret == 0)
@@ -351,33 +362,27 @@ void FileDescriptorActivity::loop()
351362
{
352363
// These variables are used in order to loop with select(). See the
353364
// while() condition below.
354-
fd_set watch_pipe;
355-
timeval timeout;
356-
char dummy;
357-
do
358-
{
359-
int unused; (void)unused;
360-
unused = read(pipe, &dummy, 1);
361-
362-
// Initialize the values for the next select() call
363-
FD_ZERO(&watch_pipe);
364-
FD_SET(pipe, &watch_pipe);
365-
timeout.tv_sec = 0;
366-
timeout.tv_usec = 0;
367-
}
368-
while(select(pipe + 1, &watch_pipe, NULL, NULL, &timeout) > 0);
365+
clearInterruptPipe();
369366
}
370367

371-
// We check the flags after the command queue was emptied as we could miss commands otherwise:
368+
// We check the flags after the command queue was emptied as we could
369+
// miss commands otherwise:
372370
bool do_trigger = true;
373371
bool user_trigger = false;
372+
bool user_timeout = false;
374373
{ RTT::os::MutexLock lock(m_command_mutex);
375-
// This section should be really fast to not block threads calling trigger(), breakLoop() or watch().
374+
// This section should be really fast to not block threads calling
375+
// trigger(), breakLoop() or watch().
376376
if (m_trigger) {
377377
do_trigger = true;
378378
user_trigger = true;
379379
m_trigger = false;
380380
}
381+
if (m_user_timeout) {
382+
do_trigger = true;
383+
user_timeout = true;
384+
m_user_timeout = false;
385+
}
381386
if (m_update_sets) {
382387
m_update_sets = false;
383388
do_trigger = false;
@@ -396,6 +401,8 @@ void FileDescriptorActivity::loop()
396401
step();
397402
if (m_has_timeout)
398403
work(RunnableInterface::TimeOut);
404+
else if ( user_timeout )
405+
work(RunnableInterface::TimeOut);
399406
else if ( user_trigger )
400407
work(RunnableInterface::Trigger);
401408
else
@@ -411,13 +418,36 @@ void FileDescriptorActivity::loop()
411418
}
412419
}
413420

421+
void FileDescriptorActivity::clearInterruptPipe() {
422+
int pipe = m_interrupt_pipe[0];
423+
424+
fd_set watch_pipe;
425+
timeval timeout;
426+
char dummy;
427+
do
428+
{
429+
int unused; (void)unused;
430+
unused = read(pipe, &dummy, 1);
431+
432+
// Initialize the values for the next select() call
433+
FD_ZERO(&watch_pipe);
434+
FD_SET(m_interrupt_pipe[0], &watch_pipe);
435+
timeout.tv_sec = 0;
436+
timeout.tv_usec = 0;
437+
}
438+
while (select(pipe + 1, &watch_pipe, NULL, NULL, &timeout) > 0);
439+
}
440+
void FileDescriptorActivity::writeInterruptPipe() {
441+
int unused; (void)unused; // avoid the "return value not used" warning
442+
unused = write(m_interrupt_pipe[1], &CMD_ANY_COMMAND, 1);
443+
}
444+
414445
bool FileDescriptorActivity::breakLoop()
415446
{
416447
{ RTT::os::MutexLock lock(m_command_mutex);
417448
m_break_loop = true;
418449
}
419-
int unused; (void)unused;
420-
unused = write(m_interrupt_pipe[1], &CMD_ANY_COMMAND, 1);
450+
writeInterruptPipe();
421451
return true;
422452
}
423453

rtt/extras/FileDescriptorActivity.hpp

+46-8
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,24 @@ namespace RTT { namespace extras {
120120
RTT::os::Mutex m_command_mutex;
121121
bool m_break_loop;
122122
bool m_trigger;
123+
bool m_user_timeout;
123124
bool m_update_sets;
124125

125126
/** Internal method that makes sure loop() takes into account
126127
* modifications in the set of watched FDs
127128
*/
128129
void triggerUpdateSets();
129130

131+
/** Internal method that writes on an internal pipe to wake up
132+
* the main loop
133+
*/
134+
void writeInterruptPipe();
135+
136+
/** Internal method that clears the interrupt pipe used to wake
137+
* up the main loop
138+
*/
139+
void clearInterruptPipe();
140+
130141
public:
131142
/**
132143
* Create a FileDescriptorActivity with a given priority and base::RunnableInterface
@@ -274,28 +285,55 @@ namespace RTT { namespace extras {
274285
*/
275286
int getTimeout_us() const;
276287

288+
/** Start the underlying thread and make it call \c loop
289+
*/
277290
virtual bool start();
291+
292+
/** The main loop, listening to various wake-up events
293+
*
294+
* The loop can be broken by calling \c breakLoop. \c timeout and \c trigger
295+
* will wake it up and make it call work with resp. a TimeOut and Trigger
296+
* reason. Available I/O on watched file descriptors will wake it up and
297+
* make it call \c work with a IOReady reason
298+
*/
278299
virtual void loop();
300+
301+
/** Wake-up \c loop and make it return */
279302
virtual bool breakLoop();
280303
virtual bool stop();
281-
282-
/** Called by loop() when data is available on the file descriptor. By
283-
* default, it calls step() on the associated runner interface (if any)
304+
305+
/** @deprecated does nothing, FileDescriptorActivity uses the \c work interface
284306
*/
285307
virtual void step();
286308

287-
/** Called by loop() when data is available on the file descriptor. By
288-
* default, it calls step() on the associated runner interface (if any)
309+
/** Called by loop() when it is woken up
310+
*
311+
* The reason parameter allows to know why the loop was woken up:
312+
* - TimeOut if the activity time out has been reached or if \c timeout
313+
* has been called. In the former case, \c hasTimeout will return true.
314+
* - IOReady is some I/O has been received on the watched file descriptors
315+
* - Trigger if trigger() has been called
316+
*
317+
* Calls runner->work with the same reason. By default \c
318+
* ExecutionEngine will call all messages, port callbacks, functions
319+
* and hooks in IOReady and TimeOut, but only messages and port
320+
* callbacks in Trigger
289321
*/
290322
virtual void work(base::RunnableInterface::WorkReason reason);
291323

292-
/** Force calling step() even if no data is available on the file
293-
* descriptor, and returns true if the signalling was successful
324+
/**
325+
* Wake up the main thread (in \c loop) and call \c work with Trigger as reason
326+
*
327+
* @return true if the activity is active, that is if it is started and
328+
* will process the trigger. false otherwise.
294329
*/
295330
virtual bool trigger();
296331

297332
/**
298-
* Always returns false.
333+
* Wake up the main thread (in \c loop) and call \c work with TimeOut as reason
334+
*
335+
* @return true if the activity is active, that is if it is started and
336+
* will process the trigger. false otherwise.
299337
*/
300338
virtual bool timeout();
301339
};

tests/specialized_activities.cpp

+20-3
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@ struct TestFDActivity : public FileDescriptorActivity
2525
int fd, other_fd, result;
2626

2727
bool do_read;
28+
base::RunnableInterface::WorkReason work_reason;
2829

2930
RTT::os::Mutex mutex;
3031

3132
TestFDActivity()
3233
: FileDescriptorActivity(0), step_count(0), count(0), other_count(0), do_read(false) {}
3334

35+
void work(base::RunnableInterface::WorkReason reason)
36+
{
37+
work_reason = reason;
38+
}
3439
void step()
3540
{
3641
RTT::os::MutexLock lock(mutex);
@@ -97,22 +102,34 @@ BOOST_AUTO_TEST_CASE( testFileDescriptorActivity )
97102
BOOST_CHECK_EQUAL(0, activity->count);
98103
BOOST_CHECK_EQUAL(0, activity->other_count);
99104
BOOST_CHECK( !activity->isRunning() && activity->isActive() );
105+
BOOST_CHECK_EQUAL(base::RunnableInterface::Trigger, activity->work_reason);
106+
107+
// Check timeout(). Disable reading as there won't be any data on the FD
108+
activity->do_read = false;
109+
BOOST_CHECK( activity->timeout() );
110+
usleep(USLEEP);
111+
BOOST_CHECK_EQUAL(2, activity->step_count);
112+
BOOST_CHECK_EQUAL(0, activity->count);
113+
BOOST_CHECK_EQUAL(0, activity->other_count);
114+
BOOST_CHECK( !activity->isRunning() && activity->isActive() );
115+
BOOST_CHECK_EQUAL(base::RunnableInterface::TimeOut, activity->work_reason);
100116

101117
// Check normal operations. Re-enable reading.
102118
activity->do_read = true;
103119
int buffer, result;
104120
result = write(writer, &buffer, 2);
105121
BOOST_CHECK( result == 2 );
106122
usleep(USLEEP);
107-
BOOST_CHECK_EQUAL(3, activity->step_count);
123+
BOOST_CHECK_EQUAL(4, activity->step_count);
108124
BOOST_CHECK_EQUAL(2, activity->count);
109125
BOOST_CHECK_EQUAL(0, activity->other_count);
110126
BOOST_CHECK( !activity->isRunning() && activity->isActive() );
127+
BOOST_CHECK_EQUAL(base::RunnableInterface::IOReady, activity->work_reason);
111128

112129
result = write(other_writer, &buffer, 2);
113130
BOOST_CHECK( result == 2 );
114131
usleep(USLEEP);
115-
BOOST_CHECK_EQUAL(5, activity->step_count);
132+
BOOST_CHECK_EQUAL(6, activity->step_count);
116133
BOOST_CHECK_EQUAL(2, activity->count);
117134
BOOST_CHECK_EQUAL(2, activity->other_count);
118135
BOOST_CHECK( !activity->isRunning() && activity->isActive() );
@@ -143,11 +160,11 @@ BOOST_AUTO_TEST_CASE( testFileDescriptorActivity )
143160
// step is blocking now
144161
// trigger another 65537 times
145162
for(std::size_t i = 0; i < 65537; ++i) activity->trigger();
163+
BOOST_CHECK_EQUAL(base::RunnableInterface::TimeOut, activity->work_reason);
146164
activity->mutex.unlock();
147165
sleep(1);
148166
BOOST_CHECK_EQUAL(2, activity->step_count);
149167
BOOST_CHECK( activity->stop() );
150-
151168
}
152169

153170

0 commit comments

Comments
 (0)