Skip to content

add poll notification support#140

Draft
obbardc wants to merge 5 commits intolibfuse:mainfrom
obbardc:wip/obbardc/add-notify-poll
Draft

add poll notification support#140
obbardc wants to merge 5 commits intolibfuse:mainfrom
obbardc:wip/obbardc/add-notify-poll

Conversation

@obbardc
Copy link
Copy Markdown

@obbardc obbardc commented May 5, 2026

Expose libfuse low-level poll support through pyfuse3.

obbardc added 2 commits May 5, 2026 10:14
Expose libfuse low-level poll support through pyfuse3 so
filesystems can implement poll(2), select(2) and epoll readiness
notifications.

Add bindings for struct fuse_pollhandle, fuse_reply_poll(),
fuse_lowlevel_notify_poll() and fuse_pollhandle_destroy(). Introduce a
Python PollHandle wrapper and a notify_poll() helper, allowing a
filesystem to retain the poll handle provided by Operations.poll() and
notify it later when readiness changes.

Wire the low-level FUSE poll callback into Operations.poll(), returning
the current readiness mask to the kernel. The default implementation
continues to raise ENOSYS so existing filesystems keep the previous
fallback behaviour unless they opt in.

This is needed by filesystems that emulate pollable kernel interfaces,
such as sysfs GPIO value files, where edge events must wake userspace
processes waiting for POLLPRI.

Fixes: libfuse#139
Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
Add a pollable file to the test filesystem and exercise the new
Operations.poll() and notify_poll() APIs.

The test opens the synthetic file, starts a userspace poll(2) waiter,
and waits until the filesystem has received and stored a PollHandle.
It then triggers readiness through the existing setxattr command
channel. The filesystem marks the file ready, calls notify_poll() and
the test verifies that the blocked poller wakes with POLLPRI.

This covers the notification path from the low-level FUSE poll callback,
through the Python PollHandle wrapper, to fuse_lowlevel_notify_poll().

Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
Comment thread src/pyfuse3/__init__.pyx Outdated
Comment thread src/pyfuse3/_pyfuse3.py
events (e.g. ``select.POLLIN``, ``select.POLLOUT``, ``select.POLLPRI``).
If no events are currently ready, return ``0``.
If *poll_handle* is not ``None``, the kernel is requesting to be
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the semantics when poll_handle is None?

Comment thread test/test_fs.py
raise


class Fs(pyfuse3.Operations):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be nicer to subclass this for different tests (i.e., define a PollTestFs), rather than aggregating all the functionality in one big class.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense - will see what I can do.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to do it. Haven't squashed the commits yet - can you review again from diff view, please ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right direction, but you're redefining almost everything instead of inheriting it. For example, your PollTestFs shouldn't need to declare its own readdir implementation, getattr implementation, etc. I think the only thing you need to overwrite is poll and setxattr.

@Nikratio
Copy link
Copy Markdown
Contributor

Nikratio commented May 5, 2026

Thanks for the MR! I only just replied on the issue tracker :-).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR exposes libfuse low-level poll(2) readiness support through pyfuse3, allowing filesystems to implement Operations.poll() and later wake blocked poll/select/epoll waiters via notify_poll().

Changes:

  • Wire up the libfuse low-level poll callback and reply path (fuse_ops.pollfuse_pollOperations.pollfuse_reply_poll).
  • Introduce a PollHandle opaque type plus notify_poll(handle) to deliver readiness notifications.
  • Add an integration test filesystem and test case covering poll notification behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/test_fs.py Adds a pollable test file and an integration test validating wakeup via notify_poll.
src/pyfuse3/internal.pxi Registers the new low-level poll handler in fuse_lowlevel_ops.
src/pyfuse3/handlers.pxi Implements fuse_poll/fuse_poll_async bridging to Operations.poll and replies with fuse_reply_poll.
src/pyfuse3/_pyfuse3.py Adds the Operations.poll() API and its documentation.
src/pyfuse3/__init__.pyx Adds PollHandle and the notify_poll() API.
src/pyfuse3/__init__.pyi Updates typing stubs for PollHandle and notify_poll().
Include/fuse_lowlevel.pxd Declares low-level poll op, fuse_reply_poll, and fuse_lowlevel_notify_poll.
Include/fuse_common.pxd Declares fuse_pollhandle and fuse_pollhandle_destroy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/pyfuse3/__init__.pyx Outdated
Comment on lines +998 to +1002
A single notification is enough to clear all pending waiters; calling
this function again on the same handle is harmless but redundant.
The handle remains valid (and may be notified again) until its Python
reference is dropped, at which point the underlying ``fuse_pollhandle``
is destroyed.
Expose libfuse low-level poll support through pyfuse3 so filesystems can
implement poll(2), select(2) and epoll readiness notifications.

Add bindings for struct fuse_pollhandle, fuse_reply_poll(),
fuse_lowlevel_notify_poll() and fuse_pollhandle_destroy(). Introduce a
Python PollHandle wrapper with a notify() method, allowing a filesystem to
retain the handle provided by Operations.poll() and wake waiters later when
readiness changes.

Wire the low-level FUSE poll callback into Operations.poll(), returning the
current readiness mask to the kernel. The default implementation continues
to raise ENOSYS so existing filesystems keep the previous fallback behaviour
unless they opt in.

This is needed by filesystems that emulate pollable kernel interfaces, such
as sysfs GPIO value files, where edge events must wake userspace processes
waiting for POLLPRI.

Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
Comment thread src/pyfuse3/__init__.pyx Outdated

cdef int ret

if self._ph is NULL:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this ever happen?

obbardc added 2 commits May 5, 2026 19:38
Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
Comment thread src/pyfuse3/__init__.pyx
def __dealloc__(self):
if self._ph is not NULL:
fuse_pollhandle_destroy(self._ph)
self._ph = NULL
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Looking at https://cython.readthedocs.io/en/latest/src/userguide/special_methods.html#finalization-methods-dealloc-and-del, nothing can possibly access the object after __dealloc__ has run, so there's no need to reset the pointer.

Comment thread src/pyfuse3/handlers.pxi
if ph is NULL:
py_ph = None
else:
py_ph = PollHandle.__new__(PollHandle)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just PollHandle()?

Comment thread test/test_fs.py
raise


class Fs(pyfuse3.Operations):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right direction, but you're redefining almost everything instead of inheriting it. For example, your PollTestFs shouldn't need to declare its own readdir implementation, getattr implementation, etc. I think the only thing you need to overwrite is poll and setxattr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants