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:

Reply via email to