LCOV - code coverage report
Current view: top level - zephyr/sys - mpsc_lockfree.h Coverage Total Hit
Test: new.info Lines: 46.7 % 15 7
Test Date: 2025-09-05 16:43:28

            Line data    Source code
       1            1 : /*
       2              :  * Copyright (c) 2010-2011 Dmitry Vyukov
       3              :  * Copyright (c) 2023 Intel Corporation
       4              :  *
       5              :  * SPDX-License-Identifier: Apache-2.0
       6              :  */
       7              : 
       8              : #ifndef ZEPHYR_SYS_MPSC_LOCKFREE_H_
       9              : #define ZEPHYR_SYS_MPSC_LOCKFREE_H_
      10              : 
      11              : #include <stdint.h>
      12              : #include <stdbool.h>
      13              : #include <zephyr/sys/atomic.h>
      14              : #include <zephyr/kernel.h>
      15              : 
      16              : #ifdef __cplusplus
      17              : extern "C" {
      18              : #endif
      19              : 
      20              : /**
      21              :  * @brief Multiple Producer Single Consumer (MPSC) Lockfree Queue API
      22              :  * @defgroup mpsc_lockfree MPSC Lockfree Queue API
      23              :  * @ingroup datastructure_apis
      24              :  * @{
      25              :  */
      26              : 
      27              : /**
      28              :  * @file mpsc_lockfree.h
      29              :  *
      30              :  * @brief A wait-free intrusive multi producer single consumer (MPSC) queue using
      31              :  * a singly linked list. Ordering is First-In-First-Out.
      32              :  *
      33              :  * Based on the well known and widely used wait-free MPSC queue described by
      34              :  * Dmitry Vyukov with some slight changes to account for needs of an
      35              :  * RTOS on a variety of archs. Both consumer and producer are wait free. No CAS
      36              :  * loop or lock is needed.
      37              :  *
      38              :  * An MPSC queue is safe to produce or consume in an ISR with O(1) push/pop.
      39              :  *
      40              :  * @warning MPSC is *not* safe to consume in multiple execution contexts.
      41              :  */
      42              : 
      43              : /*
      44              :  * On single core systems atomics are unnecessary
      45              :  * and cause a lot of unnecessary cache invalidation
      46              :  *
      47              :  * Using volatile to at least ensure memory is read/written
      48              :  * by the compiler generated op codes is enough.
      49              :  *
      50              :  * On SMP atomics *must* be used to ensure the pointers
      51              :  * are updated in the correct order.
      52              :  */
      53              : #if defined(CONFIG_SMP)
      54              : 
      55            0 : typedef atomic_ptr_t mpsc_ptr_t;
      56              : 
      57            0 : #define mpsc_ptr_get(ptr)          atomic_ptr_get(&(ptr))
      58            0 : #define mpsc_ptr_set(ptr, val)     atomic_ptr_set(&(ptr), val)
      59            0 : #define mpsc_ptr_set_get(ptr, val) atomic_ptr_set(&(ptr), val)
      60              : 
      61              : #else /* defined(CONFIG_SMP) */
      62              : 
      63              : typedef struct mpsc_node *mpsc_ptr_t;
      64              : 
      65              : #define mpsc_ptr_get(ptr)      ptr
      66              : #define mpsc_ptr_set(ptr, val) ptr = val
      67              : #define mpsc_ptr_set_get(ptr, val)                                                                 \
      68              :         ({                                                                                         \
      69              :                 mpsc_ptr_t tmp = ptr;                                                              \
      70              :                 ptr = val;                                                                         \
      71              :                 tmp;                                                                               \
      72              :         })
      73              : 
      74              : #endif /* defined(CONFIG_SMP) */
      75              : 
      76              : /**
      77              :  * @brief Queue member
      78              :  */
      79            1 : struct mpsc_node {
      80            0 :         mpsc_ptr_t next;
      81              : };
      82              : 
      83              : /**
      84              :  * @brief MPSC Queue
      85              :  */
      86            1 : struct mpsc {
      87            0 :         mpsc_ptr_t head;
      88            0 :         struct mpsc_node *tail;
      89            0 :         struct mpsc_node stub;
      90              : };
      91              : 
      92              : /**
      93              :  * @brief Static initializer for a mpsc queue
      94              :  *
      95              :  * Since the queue is
      96              :  *
      97              :  * @param symbol name of the queue
      98              :  */
      99            1 : #define MPSC_INIT(symbol)                                                                          \
     100              :         {                                                                                          \
     101              :                 .head = (struct mpsc_node *)&symbol.stub,                                          \
     102              :                 .tail = (struct mpsc_node *)&symbol.stub,                                          \
     103              :                 .stub = {                                                                          \
     104              :                         .next = NULL,                                                              \
     105              :                 },                                                                                 \
     106              :         }
     107              : 
     108              : /**
     109              :  * @brief Initialize queue
     110              :  *
     111              :  * @param q Queue to initialize or reset
     112              :  */
     113            1 : static inline void mpsc_init(struct mpsc *q)
     114              : {
     115              :         mpsc_ptr_set(q->head, &q->stub);
     116              :         q->tail = &q->stub;
     117              :         mpsc_ptr_set(q->stub.next, NULL);
     118              : }
     119              : 
     120              : /**
     121              :  * @brief Push a node
     122              :  *
     123              :  * @param q Queue to push the node to
     124              :  * @param n Node to push into the queue
     125              :  */
     126            1 : static ALWAYS_INLINE void mpsc_push(struct mpsc *q, struct mpsc_node *n)
     127              : {
     128              :         struct mpsc_node *prev;
     129              :         int key;
     130              : 
     131              :         mpsc_ptr_set(n->next, NULL);
     132              : 
     133              :         key = arch_irq_lock();
     134              :         prev = (struct mpsc_node *)mpsc_ptr_set_get(q->head, n);
     135              :         mpsc_ptr_set(prev->next, n);
     136              :         arch_irq_unlock(key);
     137              : }
     138              : 
     139              : /**
     140              :  * @brief Pop a node off of the list
     141              :  *
     142              :  * @retval NULL When no node is available
     143              :  * @retval node When node is available
     144              :  */
     145            1 : static inline struct mpsc_node *mpsc_pop(struct mpsc *q)
     146              : {
     147              :         struct mpsc_node *head;
     148              :         struct mpsc_node *tail = q->tail;
     149              :         struct mpsc_node *next = (struct mpsc_node *)mpsc_ptr_get(tail->next);
     150              : 
     151              :         /* Skip over the stub/sentinel */
     152              :         if (tail == &q->stub) {
     153              :                 if (next == NULL) {
     154              :                         return NULL;
     155              :                 }
     156              : 
     157              :                 q->tail = next;
     158              :                 tail = next;
     159              :                 next = (struct mpsc_node *)mpsc_ptr_get(next->next);
     160              :         }
     161              : 
     162              :         /* If next is non-NULL then a valid node is found, return it */
     163              :         if (next != NULL) {
     164              :                 q->tail = next;
     165              :                 return tail;
     166              :         }
     167              : 
     168              :         head = (struct mpsc_node *)mpsc_ptr_get(q->head);
     169              : 
     170              :         /* If next is NULL, and the tail != HEAD then the queue has pending
     171              :          * updates that can't yet be accessed.
     172              :          */
     173              :         if (tail != head) {
     174              :                 return NULL;
     175              :         }
     176              : 
     177              :         mpsc_push(q, &q->stub);
     178              : 
     179              :         next = (struct mpsc_node *)mpsc_ptr_get(tail->next);
     180              : 
     181              :         if (next != NULL) {
     182              :                 q->tail = next;
     183              :                 return tail;
     184              :         }
     185              : 
     186              :         return NULL;
     187              : }
     188              : 
     189              : /**
     190              :  * @}
     191              :  */
     192              : 
     193              : #ifdef __cplusplus
     194              : }
     195              : #endif
     196              : 
     197              : #endif /* ZEPHYR_SYS_MPSC_LOCKFREE_H_ */
        

Generated by: LCOV version 2.0-1