1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_CRITICAL_SECTION_H
8 #define _PICO_CRITICAL_SECTION_H
9 
10 #include "pico/lock_core.h"
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
16 /** \file critical_section.h
17  *  \defgroup critical_section critical_section
18  *  \ingroup pico_sync
19  *  \brief Critical Section API for short-lived mutual exclusion safe for IRQ and multi-core
20  *
21  *  A critical section is non-reentrant, and provides mutual exclusion using a spin-lock to prevent access
22  *  from the other core, and from (higher priority) interrupts on the same core. It does the former
23  *  using a spin lock and the latter by disabling interrupts on the calling core.
24  *
25  *  Because interrupts are disabled when a critical_section is owned, uses of the critical_section
26  *  should be as short as possible.
27  */
28 
29 typedef struct __packed_aligned critical_section {
30     spin_lock_t *spin_lock;
31     uint32_t save;
32 } critical_section_t;
33 
34 /*! \brief  Initialise a critical_section structure allowing the system to assign a spin lock number
35  *  \ingroup critical_section
36  *
37  * The critical section is initialized ready for use, and will use a (possibly shared) spin lock
38  * number assigned by the system. Note that in general it is unlikely that you would be nesting
39  * critical sections, however if you do so you *must* use \ref critical_section_init_with_lock_num
40  * to ensure that the spin lock's used are different.
41  *
42  * \param crit_sec Pointer to critical_section structure
43  */
44 void critical_section_init(critical_section_t *crit_sec);
45 
46 /*! \brief  Initialise a critical_section structure assigning a specific spin lock number
47  *  \ingroup critical_section
48  * \param crit_sec Pointer to critical_section structure
49  * \param lock_num the specific spin lock number to use
50  */
51 void critical_section_init_with_lock_num(critical_section_t *crit_sec, uint lock_num);
52 
53 /*! \brief  Enter a critical_section
54  *  \ingroup critical_section
55  *
56  * If the spin lock associated with this critical section is in use, then this
57  * method will block until it is released.
58  *
59  * \param crit_sec Pointer to critical_section structure
60  */
critical_section_enter_blocking(critical_section_t * crit_sec)61 static inline void critical_section_enter_blocking(critical_section_t *crit_sec) {
62     crit_sec->save = spin_lock_blocking(crit_sec->spin_lock);
63 }
64 
65 /*! \brief  Release a critical_section
66  *  \ingroup critical_section
67  *
68  * \param crit_sec Pointer to critical_section structure
69  */
critical_section_exit(critical_section_t * crit_sec)70 static inline void critical_section_exit(critical_section_t *crit_sec) {
71     spin_unlock(crit_sec->spin_lock, crit_sec->save);
72 }
73 
74 /*! \brief  De-Initialise a critical_section created by the critical_section_init method
75  *  \ingroup critical_section
76  *
77  * This method is only used to free the associated spin lock allocated via
78  * the critical_section_init method (it should not be used to de-initialize a spin lock
79  * created via critical_section_init_with_lock_num). After this call, the critical section is invalid
80  *
81  * \param crit_sec Pointer to critical_section structure
82  */
83 void critical_section_deinit(critical_section_t *crit_sec);
84 
85 /*! \brief Test whether a critical_section has been initialized
86  *  \ingroup mutex
87  *
88  * \param crit_sec Pointer to critical_section structure
89  * \return true if the critical section is initialized, false otherwise
90  */
critical_section_is_initialized(critical_section_t * crit_sec)91 static inline bool critical_section_is_initialized(critical_section_t *crit_sec) {
92     return crit_sec->spin_lock != 0;
93 }
94 
95 #ifdef __cplusplus
96 }
97 #endif
98 #endif
99