@@ -225,12 +225,8 @@ impl<P: Vst3Plugin> RunLoopEventHandler<P> {
225
225
self . tasks . push ( task) ?;
226
226
227
227
// 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()`.
234
230
let notify_value = 1i8 ;
235
231
const NOTIFY_VALUE_SIZE : usize = std:: mem:: size_of :: < i8 > ( ) ;
236
232
assert_eq ! (
@@ -472,21 +468,34 @@ impl<P: Vst3Plugin> IPlugViewContentScaleSupport for WrapperView<P> {
472
468
#[ cfg( target_os = "linux" ) ]
473
469
impl < P : Vst3Plugin > IEventHandler for RunLoopEventHandler < P > {
474
470
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
+
475
495
// This gets called from the host's UI thread because we wrote some bytes to the Unix domain
476
496
// socket. We'll read that data from the socket again just to make REAPER happy.
477
497
while let Some ( task) = self . tasks . pop ( ) {
478
498
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
- ) ;
490
499
}
491
500
}
492
501
}
0 commit comments