@@ -225,12 +225,8 @@ impl<P: Vst3Plugin> RunLoopEventHandler<P> {
225225 self . tasks . push ( task) ?;
226226
227227 // We need to use a Unix domain socket to let the host know to call our event handler. In
228- // theory eventfd would be more suitable here, but Ardour does not support that.
229- // XXX: This can technically lead to a race condition if the host is currently calling
230- // `on_fd_is_set()` on another thread and the task has already been popped and executed
231- // and this value has not yet been written to the socket. Doing it the other way around
232- // gets you the other situation where the event handler could be run without the task
233- // being posted yet. In practice this won't cause any issues however.
228+ // theory eventfd would be more suitable here, but Ardour does not support that. This is
229+ // read again in `Self::on_fd_is_set()`.
234230 let notify_value = 1i8 ;
235231 const NOTIFY_VALUE_SIZE : usize = std:: mem:: size_of :: < i8 > ( ) ;
236232 assert_eq ! (
@@ -472,21 +468,34 @@ impl<P: Vst3Plugin> IPlugViewContentScaleSupport for WrapperView<P> {
472468#[ cfg( target_os = "linux" ) ]
473469impl < P : Vst3Plugin > IEventHandler for RunLoopEventHandler < P > {
474470 unsafe fn on_fd_is_set ( & self , _fd : FileDescriptor ) {
471+ // There should be a one-to-one correlation to bytes being written to `self.socket_read_fd`
472+ // and events being pushed to `self.tasks`, but because the process of pushing a task and
473+ // notifying this thread through the socket is not atomic we can't reliably just read a byte
474+ // from this socket for every task we process. For instance, if `Self::post_task()` gets
475+ // called while this loop is already running, it could happen that we pop and execute the
476+ // task before the byte gets written to the socket. To avoid this, we'll clear the socket
477+ // upfront, and then execute the tasks afterwards. If this situation does happen, then the
478+ // worst thing that can happen is that this function is called a second time while
479+ // `self.tasks()` is already empty.
480+ let mut notify_value = [ 0 ; 32 ] ;
481+ loop {
482+ let read_result = libc:: read (
483+ self . socket_read_fd ,
484+ & mut notify_value as * mut _ as * mut c_void ,
485+ std:: mem:: size_of_val ( & notify_value) ,
486+ ) ;
487+
488+ // If after the first loop the socket contains no more data, then the `read()` call will
489+ // return -1 and `errno` will have been set to `EAGAIN`
490+ if read_result <= 0 {
491+ break ;
492+ }
493+ }
494+
475495 // This gets called from the host's UI thread because we wrote some bytes to the Unix domain
476496 // socket. We'll read that data from the socket again just to make REAPER happy.
477497 while let Some ( task) = self . tasks . pop ( ) {
478498 self . inner . execute ( task, true ) ;
479-
480- let mut notify_value = 1i8 ;
481- const NOTIFY_VALUE_SIZE : usize = std:: mem:: size_of :: < i8 > ( ) ;
482- assert_eq ! (
483- libc:: read(
484- self . socket_read_fd,
485- & mut notify_value as * mut _ as * mut c_void,
486- NOTIFY_VALUE_SIZE
487- ) ,
488- NOTIFY_VALUE_SIZE as isize
489- ) ;
490499 }
491500 }
492501}
0 commit comments