Fix for poll and read.

This commit is contained in:
Johannes Leupolz 2026-04-15 22:20:39 +00:00
parent 080a72906d
commit 9e55cd84ab
4 changed files with 73 additions and 29 deletions

View file

@ -106,15 +106,11 @@ fn evdev_write_watch_loop(shutdown: Arc<AtomicBool>, epoll: Arc<Epoll>) {
let state = super::state::get_vuinput_state(&fh);
if let Ok(state) = state {
let mut state = state.lock().unwrap();
for handle in state.poll.take_waiters() {
unsafe {
fuse_lowlevel::fuse_lowlevel_notify_poll(handle.as_ptr());
fuse_lowlevel::fuse_pollhandle_destroy(handle.as_ptr());
}
let handle = state.poll.take_waiters();
if let Some(mut handle)= handle {
handle.notify();
}
state.poll.pollphase = PollPhase::Readable;
state.poll.pending.clear();
}
}
}

View file

@ -11,7 +11,6 @@ use ::cuse_lowlevel::*;
use smallvec::SmallVec;
use crate::process_tools::RequestingProcess;
use smallvec::smallvec;
pub type PendingPollHandles = SmallVec<[*mut fuse_lowlevel::fuse_pollhandle; 1]>;
@ -61,6 +60,43 @@ impl Default for PollPhase {
}
}
#[derive(Debug)]
pub struct PollHandle {
ptr: NonNull<fuse_lowlevel::fuse_pollhandle>,
has_been_completed: bool,
}
impl PollHandle {
pub fn new(ptr: NonNull<fuse_lowlevel::fuse_pollhandle>) -> Self {
Self {
ptr: ptr,
has_been_completed: false,
}
}
pub fn notify(&mut self) {
if !self.has_been_completed {
unsafe {
fuse_lowlevel::fuse_lowlevel_notify_poll(self.ptr.as_ptr());
fuse_lowlevel::fuse_pollhandle_destroy(self.ptr.as_ptr());
}
self.has_been_completed = true;
}
}
}
impl Drop for PollHandle {
fn drop(&mut self) {
if !self.has_been_completed {
unsafe {
fuse_lowlevel::fuse_pollhandle_destroy(self.ptr.as_ptr());
}
self.has_been_completed = true;
}
}
}
unsafe impl Send for PollHandle {}
/// this data structure ensures poll and read are synchronized.
/// poll() and read() must synchronize through one shared readines
/// state, and the state transitions must be done under the same per-handle mutex.
@ -78,31 +114,29 @@ pub struct PollState {
/// Pending FUSE poll waiters for this device.
/// Optimized for the common case of 0 or 1 waiter, but supports
/// multiple concurrent poll() callers correctly.
pub pending: SmallVec<[NonNull<fuse_lowlevel::fuse_pollhandle>; 1]>,
pending: Option<PollHandle>,
}
impl PollState {
pub fn new() -> PollState {
PollState {
pollphase: PollPhase::Empty,
pending: smallvec![],
pending: None,
}
}
pub fn has_waiters(&self) -> bool {
!self.pending.is_empty()
!self.pending.is_some()
}
pub fn add_waiter(&mut self, handle: NonNull<fuse_lowlevel::fuse_pollhandle>) {
self.pending.push(handle);
pub fn set_waiter(&mut self, handle: NonNull<fuse_lowlevel::fuse_pollhandle>) {
self.pending = Some(PollHandle::new(handle));
}
pub fn take_waiters(&mut self) -> SmallVec<[NonNull<fuse_lowlevel::fuse_pollhandle>; 1]> {
pub fn take_waiters(&mut self) -> Option<PollHandle> {
std::mem::take(&mut self.pending)
}
}
unsafe impl Send for PollState {}
#[derive(Debug)]
pub struct VuInputState {
pub file: File,

View file

@ -5,7 +5,7 @@
use crate::cuse_device::*;
use crate::global_config::get_device_policy;
use ::cuse_lowlevel::*;
use libc::{__s32, __u16, input_event};
use libc::{__s32, __u16, input_event, POLLIN};
use libc::{off_t, size_t, EIO};
use libc::{uinput_abs_setup, uinput_setup};
use log::{debug, trace};
@ -16,13 +16,18 @@ use std::ptr::NonNull;
use uinput_ioctls::*;
// https://github.com/libfuse/libfuse/blob/master/example/poll.c
// https://github.com/torvalds/linux/blob/f82b61de0f5dc58930fdb773b9e843573fcc374b/fs/fuse/file.c
// Note that poll in fuse blocks (because it calls fuse_simple_request, which is designed to block)
// until the handle is notified.
pub unsafe extern "C" fn vuinput_poll(
req: fuse_lowlevel::fuse_req_t,
fi: *mut fuse_lowlevel::fuse_file_info,
ph: *mut fuse_lowlevel::fuse_pollhandle,
) {
fuse_lowlevel::fuse_reply_err(req, EIO);
return;
//fuse_lowlevel::fuse_reply_err(req, EIO);
//return;
let vuinput_state_mutex =
get_vuinput_state(&VuFileHandle::from_fuse_file_info(fi.as_ref().unwrap())).unwrap();
@ -32,16 +37,25 @@ pub unsafe extern "C" fn vuinput_poll(
match vuinput_state.poll.pollphase {
PollPhase::Empty => {
let ph = NonNull::<fuse_lowlevel::fuse_pollhandle>::new(ph);
vuinput_state.poll.add_waiter(ph.unwrap());
if ph != std::ptr::null_mut() {
let ph = NonNull::<fuse_lowlevel::fuse_pollhandle>::new(ph);
vuinput_state.poll.set_waiter(ph.unwrap());
}
fuse_lowlevel::fuse_reply_poll(req, 0);
}
PollPhase::Readable => {
fuse_lowlevel::fuse_lowlevel_notify_poll(ph);
fuse_lowlevel::fuse_pollhandle_destroy(ph);
if ph != std::ptr::null_mut() {
fuse_lowlevel::fuse_lowlevel_notify_poll(ph);
fuse_lowlevel::fuse_pollhandle_destroy(ph);
}
fuse_lowlevel::fuse_reply_poll(req, POLLIN.try_into().unwrap());
}
PollPhase::Reading => {
fuse_lowlevel::fuse_lowlevel_notify_poll(ph);
fuse_lowlevel::fuse_pollhandle_destroy(ph);
if ph != std::ptr::null_mut() {
fuse_lowlevel::fuse_lowlevel_notify_poll(ph);
fuse_lowlevel::fuse_pollhandle_destroy(ph);
}
fuse_lowlevel::fuse_reply_poll(req, POLLIN.try_into().unwrap());
}
}
}

View file

@ -23,8 +23,8 @@ pub unsafe extern "C" fn vuinput_read(
"vuinput_read: offset needs to be 0 but is {}",
_off
);
fuse_lowlevel::fuse_reply_err(_req, EIO);
return;
//fuse_lowlevel::fuse_reply_err(_req, EIO);
//return;
let fh = &(*_fi).fh;
let vuinput_state_mutex =
@ -41,10 +41,10 @@ pub unsafe extern "C" fn vuinput_read(
vuinput_state.poll.pollphase = PollPhase::Reading;
// read up to 24 bytes
println!("vuinput_read: read");
//println!("vuinput_read: read");
let result = vuinput_state.file.read(&mut buffer);
println!("vuinput_read: read finished");
//println!("vuinput_read: read finished");
match result {
Ok(NORMAL_SIZE) => {
if !is_compat {