Re: [gtkmm] Glib::Dispatcher::operator() reentrant?



>void DispatchNotifier::send_notification(Dispatcher* dispatcher) {
>[...]
>   gsize n_written = 0;
>
>   do {
>     void * const buffer = reinterpret_cast<guint8*>(&data) + n_written;
>     const gssize result = write(fd_sender_, buffer, sizeof(data) - 
>n_written);
>
>     if(result < 0) {
>       if(errno == EINTR)
>         continue;
>
>       warn_failed_pipe_io("write", errno);
>       return;
>     }
>
>     n_written += result;
>   }
>   while(n_written < sizeof(data));
>}
>
>Now what happens when a context switch occurs immediately after an
>incomplete write() and a second thread calls Dispatcher::operator()?
>Isn't the output into the pipe completely screwed up then?

yes. this is a broken design. the correct way to do this in a
thread-safe fashion is to use a lock-free FIFO to store the
notifications, then deliver a single byte to the pipe to wake up any
listener. a single byte write is guaranteed to either succeed or fail,
and even if it fails, and some other thread writes to the pipe in the
same way, the notification is already queued and the listener will
wake up and find it.

however, writing lock-free FIFOs for multiple writers is non-trivial,
and generally requires an atomic compare-and-swap instruction to be
used.

it be simpler to just use a lock around the multi-byte write code
shown above. this is hard, however, because for design reasons the
lock can't go *into* the code, and if it goes outside it, there is no
guarantee that all threads will use it.

--p



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]