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