This is an automated email from the ASF dual-hosted git repository. jerpelea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit efc4c773d61c416325f2f33917d8f0af0e3381ff Author: Felipe Moura <[email protected]> AuthorDate: Sat Aug 1 13:00:29 2026 -0300 drivers/sensors/sensor: pace POLLIN at the requested interval A fetch() only lower half is always ready, so a subscriber that asked for a rate with SNIOC_SET_INTERVAL got no pacing from poll(): the descriptor reported POLLIN on every pass and the application had to sleep out the period itself. That does not compose. An application polling several topics reads them sequentially from one thread, so per read sleeps serialize: three topics at 10 Hz sleeping 100 ms each yield 3.3 Hz per topic rather than 10. Pace it where poll() can act on it instead. A subscriber that never requested a rate stays always ready, and one that did becomes ready once per its own interval, driven by a watchdog armed in sensor_poll(). This is the fetch() side of what sensor_is_updated() already does for a pushing lower half, so both models now honor a requested rate the same way. The wdog_s lives in sensor_user_s rather than in the device, so each subscriber is paced at its own interval instead of at the minimum across all of them, and the timer only runs while somebody is polling. The expiry runs in timer context and takes no lock: poll_notify() is safe from an interrupt handler, and a teardown that raced it has already cleared fds, which makes both the notify and the re-arm no-ops. Teardown therefore just cancels the watchdog where it clears fds, and a watchdog keeps this off the work queue entirely, which a fetch() only sensor exists to avoid. sensor_close() needs nothing of its own: poll_setup() holds a reference on the file for the duration of the poll, so file_close() cannot run until poll_teardown() has called sensor_poll() with setup false, and that already cancelled the watchdog. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Felipe Moura <[email protected]> --- drivers/sensors/sensor.c | 54 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/sensors/sensor.c b/drivers/sensors/sensor.c index ef08737ae82..fb6b89d99f0 100644 --- a/drivers/sensors/sensor.c +++ b/drivers/sensors/sensor.c @@ -43,6 +43,7 @@ #include <nuttx/mutex.h> #include <nuttx/sensors/sensor.h> #include <nuttx/lib/lib.h> +#include <nuttx/wdog.h> /**************************************************************************** * Pre-processor Definitions @@ -96,6 +97,8 @@ struct sensor_user_s struct list_node node; /* Node of users list */ struct pollfd *fds; /* The poll structure of thread waiting events */ sensor_role_t role; /* The is used to indicate user's role based on open flags */ + struct wdog_s wdog; /* Paces POLLIN at the requested interval */ + uint64_t fetched; /* When POLLIN was last reported, in usec */ bool changed; /* This is used to indicate event happens and need to * asynchronous notify other users */ @@ -142,6 +145,7 @@ static int sensor_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); static ssize_t sensor_push_event(FAR void *priv, FAR const void *data, size_t bytes); +static void sensor_fetch_expired(wdparm_t arg); /**************************************************************************** * Private Data @@ -687,6 +691,23 @@ static void sensor_pollnotify_one(FAR struct sensor_user_s *user, poll_notify(&user->fds, 1, eventset); } +static void sensor_fetch_expired(wdparm_t arg) +{ + FAR struct sensor_user_s *user = (FAR struct sensor_user_s *)arg; + + /* Timer context, so no lock: a teardown that raced us cleared fds, which + * makes both the notify and the re-arm below no-ops. + */ + + if (user->fds != NULL) + { + user->fetched = sensor_get_timestamp(); + sensor_pollnotify_one(user, POLLIN, SENSOR_ROLE_RD); + wd_start(&user->wdog, USEC2TICK(user->state.interval), + sensor_fetch_expired, arg); + } +} + static void sensor_pollnotify(FAR struct sensor_upperhalf_s *upper, pollevent_t eventset, sensor_role_t role) { @@ -868,10 +889,7 @@ static ssize_t sensor_read(FAR struct file *filep, FAR char *buffer, return -EINVAL; } - /* Fetch the data from the device directly, there is nothing to wait - * for. This matches the POLLIN sensor_poll() always reports for a - * fetch only sensor. - */ + /* Read the device directly, there is nothing to wait for */ if (!upper->state.nsubscribers) { @@ -1174,12 +1192,31 @@ static int sensor_poll(FAR struct file *filep, fds->priv = filep; if (lower->ops->fetch) { - /* Always return POLLIN for fetch only sensor: the data is read - * from the device on demand by sensor_read(), so there is never - * anything to wait for. + /* Always ready, unless a rate was requested: then once per + * interval, woken by sensor_fetch_expired(). */ - eventset |= POLLIN; + if (user->state.interval == UINT32_MAX) + { + eventset |= POLLIN; + } + else + { + uint64_t now = sensor_get_timestamp(); + uint64_t elapsed = now - user->fetched; + + if (elapsed >= user->state.interval) + { + user->fetched = now; + eventset |= POLLIN; + } + else + { + wd_start(&user->wdog, + USEC2TICK(user->state.interval - elapsed), + sensor_fetch_expired, (wdparm_t)user); + } + } } else if (sensor_is_updated(upper, user)) { @@ -1197,6 +1234,7 @@ static int sensor_poll(FAR struct file *filep, { user->fds = NULL; fds->priv = NULL; + wd_cancel(&user->wdog); } errout:
