TNeo  v1.08
tn_mutex.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeo: real-time kernel initially based on TNKernel
4  *
5  * TNKernel: copyright 2004, 2013 Yuri Tiomkin.
6  * PIC32-specific routines: copyright 2013, 2014 Anders Montonen.
7  * TNeo: copyright 2014 Dmitry Frank.
8  *
9  * TNeo was born as a thorough review and re-implementation of
10  * TNKernel. The new kernel has well-formed code, inherited bugs are fixed
11  * as well as new features being added, and it is tested carefully with
12  * unit-tests.
13  *
14  * API is changed somewhat, so it's not 100% compatible with TNKernel,
15  * hence the new name: TNeo.
16  *
17  * Permission to use, copy, modify, and distribute this software in source
18  * and binary forms and its documentation for any purpose and without fee
19  * is hereby granted, provided that the above copyright notice appear
20  * in all copies and that both that copyright notice and this permission
21  * notice appear in supporting documentation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY FRANK OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 /**
38  * \file
39  *
40  * A mutex is an object used to protect shared resources.
41  *
42  * There is a lot of confusion about the differences between semaphores and
43  * mutexes, so, it's highly recommended that you read a small article by
44  * Michael Barr: [Mutexes and Semaphores Demystified](http://goo.gl/YprPBW).
45  *
46  * Very short:
47  *
48  * While a mutex is seemingly similar to a semaphore with a maximum count of `1`
49  * (the so-called binary semaphore), their usage is very different: the purpose
50  * of mutex is to protect a shared resource. A locked mutex is "owned" by the
51  * task that locked it, and only that same task may unlock it. This ownership
52  * allows you to implement algorithms to prevent priority inversion. So,
53  * a mutex is a *locking mechanism*.
54  *
55  * A semaphore, on the other hand, is a *signaling mechanism*. It's quite legal
56  * and encouraged for a semaphore to be acquired in task A, and then signaled
57  * from task B or even from an ISR. It may be used in situations like "producer
58  * and consumer", etc.
59  *
60  * In addition to the article mentioned above, you may want to look at the
61  * [related question on stackoverflow.com](http://goo.gl/ZBReHK).
62  *
63  * ---------------------------------------------------------------------------
64  *
65  * Mutex features in TNeo:
66  *
67  * - Recursive locking is supported (if option `#TN_MUTEX_REC` is non-zero);
68  * - Deadlock detection (if option `#TN_MUTEX_DEADLOCK_DETECT` is non-zero);
69  * - Two protocols available to avoid unbounded priority inversion: priority
70  * inheritance and priority ceiling.
71  *
72  *
73  * A discussion about the strengths and weaknesses of each protocol as
74  * well as the priority inversions problem is beyond the scope of this document.
75  *
76  * The priority inheritance protocol solves the priority inversion problem, but
77  * doesn't prevent deadlocks. However, the kernel can notify you if a deadlock
78  * has occurred (see `#TN_MUTEX_DEADLOCK_DETECT`).
79  *
80  * The priority ceiling protocol prevents deadlocks and chained blocking but it
81  * is slower than the priority inheritance protocol.
82  *
83  * @see `#TN_USE_MUTEXES`
84  */
85 
86 #ifndef _TN_MUTEX_H
87 #define _TN_MUTEX_H
88 
89 /*******************************************************************************
90  * INCLUDED FILES
91  ******************************************************************************/
92 
93 #include "tn_list.h"
94 #include "tn_common.h"
95 
96 
97 
98 #ifdef __cplusplus
99 extern "C" { /*}*/
100 #endif
101 
102 /*******************************************************************************
103  * PUBLIC TYPES
104  ******************************************************************************/
105 
106 /**
107  * Mutex protocol for avoid priority inversion
108  */
110  ///
111  /// Mutex uses priority ceiling protocol
113  ///
114  /// Mutex uses priority inheritance protocol
116 };
117 
118 
119 /**
120  * Mutex
121  */
122 struct TN_Mutex {
123  ///
124  /// id for object validity verification.
125  /// This field is in the beginning of the structure to make it easier
126  /// to detect memory corruption.
128  ///
129  /// List of tasks that wait a mutex
131  ///
132  /// To include in task's locked mutexes list (if any)
134 #if TN_MUTEX_DEADLOCK_DETECT
135  ///
136  /// List of other mutexes involved in deadlock
137  /// (normally, this list is empty)
139 #endif
140  ///
141  /// Mutex protocol: priority ceiling or priority inheritance
143  ///
144  /// Current mutex owner (task that locked mutex)
145  struct TN_Task *holder;
146  ///
147  /// Used if only protocol is `#TN_MUTEX_PROT_CEILING`:
148  /// maximum priority of task that may lock the mutex
150  ///
151  /// Lock count (for recursive locking)
152  int cnt;
153 };
154 
155 /*******************************************************************************
156  * PROTECTED GLOBAL DATA
157  ******************************************************************************/
158 
159 /*******************************************************************************
160  * DEFINITIONS
161  ******************************************************************************/
162 
163 /*******************************************************************************
164  * PUBLIC FUNCTION PROTOTYPES
165  ******************************************************************************/
166 
167 /**
168  * Construct the mutex. The field `id_mutex` should not contain `#TN_ID_MUTEX`,
169  * otherwise, `#TN_RC_WPARAM` is returned.
170  *
171  * $(TN_CALL_FROM_TASK)
172  * $(TN_CALL_FROM_ISR)
173  * $(TN_LEGEND_LINK)
174  *
175  * @param mutex
176  * Pointer to already allocated `struct TN_Mutex`
177  * @param protocol
178  * Mutex protocol: priority ceiling or priority inheritance.
179  * See `enum #TN_MutexProtocol`.
180  * @param ceil_priority
181  * Used if only `protocol` is `#TN_MUTEX_PROT_CEILING`: maximum priority
182  * of the task that may lock the mutex.
183  *
184  * @return
185  * * `#TN_RC_OK` if mutex was successfully created;
186  * * If `#TN_CHECK_PARAM` is non-zero, additional return code
187  * is available: `#TN_RC_WPARAM`.
188  */
190  struct TN_Mutex *mutex,
192  int ceil_priority
193  );
194 
195 /**
196  * Destruct mutex.
197  *
198  * All tasks that wait for lock the mutex become runnable with
199  * `#TN_RC_DELETED` code returned.
200  *
201  * $(TN_CALL_FROM_TASK)
202  * $(TN_CAN_SWITCH_CONTEXT)
203  * $(TN_LEGEND_LINK)
204  *
205  * @param mutex mutex to destruct
206  *
207  * @return
208  * * `#TN_RC_OK` if mutex was successfully destroyed;
209  * * `#TN_RC_WCONTEXT` if called from wrong context;
210  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
211  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
212  */
213 enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex);
214 
215 /**
216  * Lock mutex.
217  *
218  * * If the mutex is not locked, function immediately locks the mutex and
219  * returns `#TN_RC_OK`.
220  * * If the mutex is already locked by the same task, lock count is merely
221  * incremented and `#TN_RC_OK` is returned immediately.
222  * * If the mutex is locked by different task, behavior depends on
223  * `timeout` value: refer to `#TN_TickCnt`.
224  *
225  * $(TN_CALL_FROM_TASK)
226  * $(TN_CAN_SWITCH_CONTEXT)
227  * $(TN_CAN_SLEEP)
228  * $(TN_LEGEND_LINK)
229  *
230  * @param mutex mutex to lock
231  * @param timeout refer to `#TN_TickCnt`
232  *
233  * @return
234  * * `#TN_RC_OK` if mutex is successfully locked or if lock count was
235  * merely incremented (this is possible if recursive locking is enabled,
236  * see `#TN_MUTEX_REC`)
237  * * `#TN_RC_WCONTEXT` if called from wrong context;
238  * * `#TN_RC_ILLEGAL_USE`
239  * * if mutex protocol is `#TN_MUTEX_PROT_CEILING`
240  * and calling task's priority is higher than `ceil_priority`
241  * given to `tn_mutex_create()`
242  * * if recursive locking is disabled (see `#TN_MUTEX_REC`)
243  * and the mutex is already locked by calling task
244  * * Other possible return codes depend on `timeout` value,
245  * refer to `#TN_TickCnt`
246  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
247  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
248  *
249  * @see `#TN_MutexProtocol`
250  */
251 enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_TickCnt timeout);
252 
253 /**
254  * The same as `tn_mutex_lock()` with zero timeout
255  *
256  * $(TN_CALL_FROM_TASK)
257  * $(TN_CAN_SWITCH_CONTEXT)
258  * $(TN_LEGEND_LINK)
259  */
260 enum TN_RCode tn_mutex_lock_polling(struct TN_Mutex *mutex);
261 
262 /**
263  * Unlock mutex.
264  * * If mutex is not locked or locked by different task, `#TN_RC_ILLEGAL_USE`
265  * is returned.
266  * * If mutex is already locked by calling task, lock count is decremented.
267  * Now, if lock count is zero, mutex gets unlocked (and if there are
268  * task(s) waiting for mutex, the first one from the wait queue locks the
269  * mutex). Otherwise, mutex remains locked with lock count decremented
270  * and function returns `#TN_RC_OK`.
271  *
272  * $(TN_CALL_FROM_TASK)
273  * $(TN_CAN_SWITCH_CONTEXT)
274  * $(TN_LEGEND_LINK)
275  *
276  * @return
277  * * `#TN_RC_OK` if mutex is unlocked of if lock count was merely decremented
278  * (this is possible if recursive locking is enabled, see `#TN_MUTEX_REC`)
279  * * `#TN_RC_WCONTEXT` if called from wrong context;
280  * * `#TN_RC_ILLEGAL_USE` if mutex is either not locked or locked by
281  * different task
282  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
283  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
284  *
285  */
286 enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex);
287 
288 
289 #ifdef __cplusplus
290 } /* extern "C" */
291 #endif
292 
293 #endif // _TN_MUTEX_H
294 
295 /*******************************************************************************
296  * end of file
297  ******************************************************************************/
298 
299 
TN_MutexProtocol
Mutex protocol for avoid priority inversion.
Definition: tn_mutex.h:109
enum TN_RCode tn_mutex_create(struct TN_Mutex *mutex, enum TN_MutexProtocol protocol, int ceil_priority)
Construct the mutex.
Circular doubly linked list, for internal kernel usage.
Mutex.
Definition: tn_mutex.h:122
Task.
Definition: tn_tasks.h:330
TN_RCode
Result code returned by kernel services.
Definition: tn_common.h:81
enum TN_RCode tn_mutex_lock_polling(struct TN_Mutex *mutex)
The same as tn_mutex_lock() with zero timeout.
enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex)
Unlock mutex.
TN_ObjId
Magic number for object validity verification.
Definition: tn_common.h:65
unsigned long TN_TickCnt
Type for system tick count, it is used by the kernel to represent absolute tick count value as well a...
Definition: tn_common.h:188
Definitions used through the whole kernel.
enum TN_MutexProtocol protocol
Mutex protocol: priority ceiling or priority inheritance.
Definition: tn_mutex.h:142
struct TN_ListItem deadlock_list
List of other mutexes involved in deadlock (normally, this list is empty)
Definition: tn_mutex.h:138
enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex)
Destruct mutex.
struct TN_ListItem mutex_queue
To include in task's locked mutexes list (if any)
Definition: tn_mutex.h:133
enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_TickCnt timeout)
Lock mutex.
int cnt
Lock count (for recursive locking)
Definition: tn_mutex.h:152
enum TN_ObjId id_mutex
id for object validity verification.
Definition: tn_mutex.h:127
int ceil_priority
Used if only protocol is TN_MUTEX_PROT_CEILING: maximum priority of task that may lock the mutex...
Definition: tn_mutex.h:149
Mutex uses priority ceiling protocol.
Definition: tn_mutex.h:112
Mutex uses priority inheritance protocol.
Definition: tn_mutex.h:115
Circular doubly linked list item, for internal kernel usage.
Definition: tn_list.h:63
struct TN_Task * holder
Current mutex owner (task that locked mutex)
Definition: tn_mutex.h:145
struct TN_ListItem wait_queue
List of tasks that wait a mutex.
Definition: tn_mutex.h:130