Hello,
I'm using dbus-cxx-0.9.0 for a project and I noticed a small inaccuracy in the handling of write watches.
When
DBus::Dispatcher gets notified of a newly added watch, it inserts the watch into its member
m_watches_map that is a
std::map storing watch pointers ordered by fd.
This map is used by the function
Dispatcher::handle_read_and_write_watches(std::vector<struct pollfd>* fds) to get the pointer to the watch object having a specific fd and perform
handle_read()/
handle_write() on it.
Right after the creation and initialization of a new
DBus::Connection,
DBus::Dispatcher gets notified of two newly added watches to be monitored: one write watch and one read watch having both
the same file descriptor.
Initially the write watch is disabled and the read watch is enabled, so the Dispatcher's main loop polls only for
POLLIN events on that file descriptor. When the write watch gets enabled the
POLLOUT events are also monitored on that fd.
From what I saw, after the initialization of a connection, DBus always notifies the addition of the write watch first and then that of the read watch.
So the Dispatcher proceeds to insert the write watch first and the read watch after in its watch map (
m_watches_map).
But since keys in a
std::map container are
unique, the inserion of the read watch with same fd (i.e. map key) overwrites that of the write watch that occurred before.
Most of the time DBus reliles on the read watch only, so the
handle_read_and_write_watches(...) function will call the correct
handle_read() operation on the read watch found in the
m_watches_map.
But in those sporadic cases in which DBus enables the write watch, the
handle_write() operation is erroneously performed on the read watch instead of the write watch that is not stored in
m_watches_map (because it has been replaced).
The simple solution to this problem is to make
m_watches_map an
std::multimap so it can store two nodes with the same key (fd) but having as values the pointers to the two read & write watches.
So in
dispatcher.h, line 97, we have to convert Dispatcher's member
m_watches_map to a
multimap:
std::multimap<int,Watch::pointer> m_watches_map;
and in
dispatcher.cpp we need to modify
handle_read_and_write_watches(..) (line 202) and
on_add_watch(..) (line 260) as follows:
void Dispatcher::handle_read_and_write_watches( std::vector<struct pollfd>* fds ){
for ( const struct pollfd& fd : *fds ){
if( (fd.events == POLLIN) &&
(fd.revents & POLLIN ) ){
std::lock_guard<std::mutex> watch_lock( m_mutex_watches );
auto range = m_watches_map.equal_range(fd.fd);
for (auto i = range.first; i != range.second; ++i)
{
Watch::pointer watch = i->second;
if( watch->is_readable() ) {
watch->handle_read();
}
}
}else if( (fd.events == POLLOUT) &&
(fd.revents & POLLOUT ) ){
std::lock_guard<std::mutex> watch_lock( m_mutex_watches );
auto range = m_watches_map.equal_range(fd.fd);
for (auto i = range.first; i != range.second; ++i)
{
Watch::pointer watch = i->second;
if( watch->is_writable() ) {
watch->handle_write();
}
}
}
}
}
bool Dispatcher::on_add_watch(Watch::pointer watch)
{
if ( not watch or not watch->is_valid() ) return false;
SIMPLELOGGER_DEBUG( "dbus.Dispatcher", "add watch fd:" << watch->unix_fd() );
std::lock_guard<std::mutex> watch_lock( m_mutex_watches );
m_watches.push_back( watch );
m_watches_map.insert( std::pair<int,Watch::pointer>(watch->unix_fd(),watch) );
wakeup_thread();
return true;
}
Please let me know if you consider this analysis correct.
Hope this helps.
Best regards,
Marco Nicolosi