LCOV - code coverage report
Current view: top level - zephyr/sys - mpsc_lockfree.h Hit Total Coverage
Test: new.info Lines: 7 15 46.7 %
Date: 2024-12-22 00:14:23

          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 1.14