1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_SEM_H
8 #define _PICO_SEM_H
9 
10 #include "pico/lock_core.h"
11 
12 /** \file sem.h
13  *  \defgroup sem sem
14  *  \ingroup pico_sync
15  *  \brief Semaphore API for restricting access to a resource
16  *
17  * A semaphore holds a number of available permits. `sem_acquire` methods will acquire a permit if available
18  * (reducing the available count by 1) or block if the number of available permits is 0.
19  * \ref sem_release() increases the number of available permits by one potentially unblocking a `sem_acquire` method.
20  *
21  * Note that \ref sem_release() may be called an arbitrary number of times, however the number of available
22  * permits is capped to the max_permit value specified during semaphore initialization.
23  *
24  * Although these semaphore related functions can be used from IRQ handlers, it is obviously preferable to only
25  * release semaphores from within an IRQ handler (i.e. avoid blocking)
26  */
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 typedef struct semaphore {
32     struct lock_core core;
33     int16_t permits;
34     int16_t max_permits;
35 } semaphore_t;
36 
37 
38 /*! \brief  Initialise a semaphore structure
39  *  \ingroup sem
40  *
41  * \param sem Pointer to semaphore structure
42  * \param initial_permits How many permits are initially acquired
43  * \param max_permits  Total number of permits allowed for this semaphore
44  */
45 void sem_init(semaphore_t *sem, int16_t initial_permits, int16_t max_permits);
46 
47 /*! \brief  Return number of available permits on the semaphore
48  *  \ingroup sem
49  *
50  * \param sem Pointer to semaphore structure
51  * \return The number of permits available on the semaphore.
52  */
53 int sem_available(semaphore_t *sem);
54 
55 /*! \brief  Release a permit on a semaphore
56  *  \ingroup sem
57  *
58  * Increases the number of permits by one (unless the number of permits is already at the maximum).
59  * A blocked `sem_acquire` will be released if the number of permits is increased.
60  *
61  * \param sem Pointer to semaphore structure
62  * \return true if the number of permits available was increased.
63  */
64 bool sem_release(semaphore_t *sem);
65 
66 /*! \brief  Reset semaphore to a specific number of available permits
67  *  \ingroup sem
68  *
69  * Reset value should be from 0 to the max_permits specified in the init function
70  *
71  * \param sem Pointer to semaphore structure
72  * \param permits the new number of available permits
73  */
74 void sem_reset(semaphore_t *sem, int16_t permits);
75 
76 /*! \brief  Acquire a permit from the semaphore
77  *  \ingroup sem
78  *
79  * This function will block and wait if no permits are available.
80  *
81  * \param sem Pointer to semaphore structure
82  */
83 void sem_acquire_blocking(semaphore_t *sem);
84 
85 /*! \brief  Acquire a permit from a semaphore, with timeout
86  *  \ingroup sem
87  *
88  * This function will block and wait if no permits are available, until the
89  * defined timeout has been reached. If the timeout is reached the function will
90  * return false, otherwise it will return true.
91  *
92  * \param sem Pointer to semaphore structure
93  * \param timeout_ms Time to wait to acquire the semaphore, in milliseconds.
94  * \return false if timeout reached, true if permit was acquired.
95  */
96 bool sem_acquire_timeout_ms(semaphore_t *sem, uint32_t timeout_ms);
97 
98 /*! \brief  Acquire a permit from a semaphore, with timeout
99  *  \ingroup sem
100  *
101  * This function will block and wait if no permits are available, until the
102  * defined timeout has been reached. If the timeout is reached the function will
103  * return false, otherwise it will return true.
104  *
105  * \param sem Pointer to semaphore structure
106  * \param timeout_us Time to wait to acquire the semaphore, in microseconds.
107  * \return false if timeout reached, true if permit was acquired.
108  */
109 bool sem_acquire_timeout_us(semaphore_t *sem, uint32_t timeout_us);
110 
111 /*! \brief Wait to acquire a permit from a semaphore until a specific time
112  *  \ingroup sem
113  *
114  * This function will block and wait if no permits are available, until the
115  * specified timeout time. If the timeout is reached the function will
116  * return false, otherwise it will return true.
117  *
118  * \param sem Pointer to semaphore structure
119  * \param until The time after which to return if the sem is not available.
120  * \return true if permit was acquired, false if the until time was reached before
121  * acquiring.
122  */
123 bool sem_acquire_block_until(semaphore_t *sem, absolute_time_t until);
124 
125 /*! \brief Attempt to acquire a permit from a semaphore without blocking
126  *  \ingroup sem
127  *
128  * This function will return false without blocking if no permits are
129  * available, otherwise it will acquire a permit and return true.
130  *
131  * \param sem Pointer to semaphore structure
132  * \return true if permit was acquired.
133  */
134 bool sem_try_acquire(semaphore_t *sem);
135 
136 #ifdef __cplusplus
137 }
138 #endif
139 #endif
140