1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
|
/*
*
* (C) COPYRIGHT 2010-2011 ARM Limited. All rights reserved.
*
* This program is free software and is provided to you under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
*
* A copy of the licence is included with the program, and can also be obtained from Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _OSK_LOCKS_H_
#define _OSK_LOCKS_H_
#ifndef _OSK_H_
#error "Include mali_osk.h directly"
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @addtogroup base_api
* @{
*/
/**
* @addtogroup base_osk_api
* @{
*/
/**
* @defgroup osklocks Mutual Exclusion
*
* A read/write lock (rwlock) is used to control access to a shared resource,
* where multiple threads are allowed to read from the shared resource, but
* only one thread is allowed to write to the shared resources at any one time.
* A thread must specify the type of access (read/write) when locking the
* rwlock. If a rwlock is locked for write access, other threads that attempt
* to lock the same rwlock will block. If a rwlock is locked for read access,
* threads that attempts to lock the rwlock for write access, will block until
* until all threads with read access have unlocked the rwlock.
* @note If an OS does not provide a synchronisation object to implement a
* rwlock, a OSK mutex can be used instead for its implementation. This would
* only allow one reader or writer to access the shared resources at any one
* time.
*
* A mutex is used to control access to a shared resource, where only one
* thread is allowed access at any one time. A thread must lock the mutex
* to gain access; other threads that attempt to lock the same mutex will
* block. Mutexes can only be unlocked by the thread that holds the lock.
*
* @note OSK mutexes are intended for use in a situation where access to the
* shared resource is likely to be contended. OSK mutexes make use of the
* mutual exclusion primitives provided by the target OS, which often
* are considered "heavyweight".
*
* Spinlocks are also used to control access to a shared resource and
* enforce that only one thread has access at any one time. They differ from
* OSK mutexes in that they poll the mutex to obtain the lock. This makes a
* spinlock especially suited for contexts where you are not allowed to block
* while waiting for access to the shared resource. A OSK mutex could not be
* used in such a context as it can block while trying to obtain the mutex.
*
* A spinlock should be held for the minimum time possible, as in the contended
* case threads will not sleep but poll and therefore use CPU-cycles.
*
* While holding a spinlock, you must not sleep. You must not obtain a rwlock,
* mutex or do anything else that might block your thread. This is to prevent another
* thread trying to lock the same spinlock while your thread holds the spinlock,
* which could take a very long time (as it requires your thread to get scheduled
* in again and unlock the spinlock) or could even deadlock your system.
*
* Spinlocks are considered 'lightweight': for the uncontended cases, the mutex
* can be obtained quickly. For the lightly-contended cases on Multiprocessor
* systems, the mutex can be obtained quickly without resorting to
* "heavyweight" OS primitives.
*
* Two types of spinlocks are provided. A type that is safe to use when sharing
* a resource with an interrupt service routine, and one that should only be
* used to share the resource between threads. The former should be used to
* prevent deadlock between a thread that holds a spinlock while an
* interrupt occurs and the interrupt service routine trying to obtain the same
* spinlock too.
*
* @anchor oskmutex_spinlockdetails
* @par Important details of OSK Spinlocks.
*
* OSK spinlocks are not intended for high-contention cases. If high-contention
* usecases occurs frequently for a particular spinlock, then it is wise to
* consider using an OSK Mutex instead.
*
* @note An especially important reason for not using OSK Spinlocks in highly
* contended cases is that they defeat the OS's Priority Inheritance mechanisms
* that would normally alleviate Priority Inversion problems. This is because
* once the spinlock is obtained, the OS usually does not know which thread has
* obtained the lock, and so cannot know which thread must have its priority
* boosted to alleviate the Priority Inversion.
*
* As a guide, use a spinlock when CPU-bound for a short period of time
* (thousands of cycles). CPU-bound operations include reading/writing of
* memory or registers. Do not use a spinlock when IO bound (e.g. user input,
* buffered IO reads/writes, calls involving significant device driver IO
* calls).
*/
/** @{ */
/**
* @brief Initialize a mutex
*
* Initialize a mutex structure. If the function returns successfully, the
* mutex is in the unlocked state.
*
* The caller must allocate the memory for the @see osk_mutex
* structure, which is then populated within this function. If the OS-specific
* mutex referenced from the structure cannot be initialized, an error is
* returned.
*
* The mutex must be terminated when no longer required, by using
* osk_mutex_term(). Otherwise, a resource leak may result in the OS.
*
* The mutex is initialized with a lock order parameter, \a order. Refer to
* @see oskmutex_lockorder for more information on Rwlock/Mutex/Spinlock lock
* ordering.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to initialize a mutex that is
* currently initialized.
*
* @param[out] lock pointer to an uninitialized mutex structure
* @param[in] order the locking order of the mutex
* @return OSK_ERR_NONE on success, any other value indicates a failure.
*/
OSK_STATIC_INLINE osk_error osk_mutex_init(osk_mutex * const lock, osk_lock_order order) CHECK_RESULT;
/**
* @brief Terminate a mutex
*
* Terminate the mutex pointed to by \a lock, which must be
* a pointer to a valid unlocked mutex. When the mutex is terminated, the
* OS-specific mutex is freed.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to terminate a mutex that is currently
* terminated.
*
* @illegal It is illegal to call osk_mutex_term() on a locked mutex.
*
* @param[in] lock pointer to a valid mutex structure
*/
OSK_STATIC_INLINE void osk_mutex_term(osk_mutex * lock);
/**
* @brief Lock a mutex
*
* Lock the mutex pointed to by \a lock. If the mutex is currently unlocked,
* the calling thread returns with the mutex locked. If a second thread
* attempts to lock the same mutex, it blocks until the first thread
* unlocks the mutex. If two or more threads are blocked waiting on the first
* thread to unlock the mutex, it is undefined as to which thread is unblocked
* when the first thread unlocks the mutex.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to lock a mutex or spinlock with an order that is
* higher than any mutex or spinlock held by the current thread. Mutexes and
* spinlocks must be locked in the order of highest to lowest, to prevent
* deadlocks. Refer to @see oskmutex_lockorder for more information.
*
* It is a programming error to exit a thread while it has a locked mutex.
*
* It is a programming error to lock a mutex from an ISR context. In an ISR
* context you are not allowed to block what osk_mutex_lock() potentially does.
*
* @illegal It is illegal to call osk_mutex_lock() on a mutex that is currently
* locked by the caller thread. That is, it is illegal for the same thread to
* lock a mutex twice, without unlocking it in between.
*
* @param[in] lock pointer to a valid mutex structure
*/
OSK_STATIC_INLINE void osk_mutex_lock(osk_mutex * lock);
/**
* @brief Unlock a mutex
*
* Unlock the mutex pointed to by \a lock. The calling thread must be the
* same thread that locked the mutex. If no other threads are waiting on the
* mutex to be unlocked, the function returns immediately, with the mutex
* unlocked. If one or more threads are waiting on the mutex to be unlocked,
* then this function returns, and a thread waiting on the mutex can be
* unblocked. It is undefined as to which thread is unblocked.
*
* @note It is not defined \em when a waiting thread is unblocked. For example,
* a thread calling osk_mutex_unlock() followed by osk_mutex_lock() may (or may
* not) obtain the lock again, preventing other threads from being
* released. Neither the 'immediately releasing', nor the 'delayed releasing'
* behavior of osk_mutex_unlock() can be relied upon. If such behavior is
* required, then you must implement it yourself, such as by using a second
* synchronization primitive.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* @illegal It is illegal for a thread to call osk_mutex_unlock() on a mutex
* that it has not locked, even if that mutex is currently locked by another
* thread. That is, it is illegal for any thread other than the 'owner' of the
* mutex to unlock it. And, you must not unlock an already unlocked mutex.
*
* @param[in] lock pointer to a valid mutex structure
*/
OSK_STATIC_INLINE void osk_mutex_unlock(osk_mutex * lock);
/**
* @brief Initialize a spinlock
*
* Initialize a spinlock. If the function returns successfully, the
* spinlock is in the unlocked state.
*
* @note If the spinlock is used for sharing a resource with an interrupt service
* routine, use the IRQ safe variant of the spinlock, see osk_spinlock_irq.
* The IRQ safe variant should be used in that situation to prevent
* deadlock between a thread/ISR that holds a spinlock while an interrupt occurs
* and the interrupt service routine trying to obtain the same spinlock too.
* The caller must allocate the memory for the @see osk_spinlock
* structure, which is then populated within this function. If the OS-specific
* spinlock referenced from the structure cannot be initialized, an error is
* returned.
*
* The spinlock must be terminated when no longer required, by using
* osk_spinlock_term(). Otherwise, a resource leak may result in the OS.
*
* The spinlock is initialized with a lock order parameter, \a order. Refer to
* @see oskmutex_lockorder for more information on Rwlock/Mutex/Spinlock lock
* ordering.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to initialize a spinlock that is
* currently initialized.
*
* @param[out] lock pointer to a spinlock structure
* @param[in] order the locking order of the spinlock
* @return OSK_ERR_NONE on success, any other value indicates a failure.
*/
OSK_STATIC_INLINE osk_error osk_spinlock_init(osk_spinlock * const lock, osk_lock_order order) CHECK_RESULT;
/**
* @brief Terminate a spinlock
*
* Terminates the spinlock and releases any associated resources.
* The spinlock must be in an unlocked state.
*
* Terminate the spinlock pointed to by \a lock, which must be
* a pointer to a valid unlocked spinlock. When the spinlock is terminated, the
* OS-specific spinlock is freed.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to terminate a spinlock that is currently
* terminated.
*
* @illegal It is illegal to call osk_spinlock_term() on a locked spinlock.
* @param[in] lock pointer to a valid spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_term(osk_spinlock * lock);
/**
* @brief Lock a spinlock
*
* Lock the spinlock pointed to by \a lock. If the spinlock is currently unlocked,
* the calling thread returns with the spinlock locked. If a second thread
* attempts to lock the same spinlock, it polls the spinlock until the first thread
* unlocks the spinlock. If two or more threads are polling the spinlock waiting
* on the first thread to unlock the spinlock, it is undefined as to which thread
* will lock the spinlock when the first thread unlocks the spinlock.
*
* While the spinlock is locked by the calling thread, the spinlock implementation
* should prevent any possible deadlock issues arising from another thread on the
* same CPU trying to lock the same spinlock.
*
* While holding a spinlock, you must not sleep. You must not obtain a rwlock,
* mutex or do anything else that might block your thread. This is to prevent another
* thread trying to lock the same spinlock while your thread holds the spinlock,
* which could take a very long time (as it requires your thread to get scheduled
* in again and unlock the spinlock) or could even deadlock your system.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to lock a spinlock, rwlock or mutex with an order that
* is higher than any spinlock, rwlock, or mutex held by the current thread. Spinlocks,
* Rwlocks, and Mutexes must be locked in the order of highest to lowest, to prevent
* deadlocks. Refer to @see oskmutex_lockorder for more information.
*
* It is a programming error to exit a thread while it has a locked spinlock.
*
* It is a programming error to lock a spinlock from an ISR context. Use the IRQ
* safe spinlock type instead.
*
* @illegal It is illegal to call osk_spinlock_lock() on a spinlock that is currently
* locked by the caller thread. That is, it is illegal for the same thread to
* lock a spinlock twice, without unlocking it in between.
*
* @param[in] lock pointer to a valid spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_lock(osk_spinlock * lock);
/**
* @brief Unlock a spinlock
*
* Unlock the spinlock pointed to by \a lock. The calling thread must be the
* same thread that locked the spinlock. If no other threads are polling the
* spinlock waiting on the spinlock to be unlocked, the function returns
* immediately, with the spinlock unlocked. If one or more threads are polling
* the spinlock waiting on the spinlock to be unlocked, then this function
* returns, and a thread waiting on the spinlock can stop polling and continue
* with the spinlock locked. It is undefined as to which thread this is.
*
* @note It is not defined \em when a waiting thread continues. For example,
* a thread calling osk_spinlock_unlock() followed by osk_spinlock_lock() may (or may
* not) obtain the spinlock again, preventing other threads from continueing.
* Neither the 'immediately releasing', nor the 'delayed releasing'
* behavior of osk_spinlock_unlock() can be relied upon. If such behavior is
* required, then you must implement it yourself, such as by using a second
* synchronization primitive.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* @illegal It is illegal for a thread to call osk_spinlock_unlock() on a spinlock
* that it has not locked, even if that spinlock is currently locked by another
* thread. That is, it is illegal for any thread other than the 'owner' of the
* spinlock to unlock it. And, you must not unlock an already unlocked spinlock.
*
* @param[in] lock pointer to a valid spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_unlock(osk_spinlock * lock);
/**
* @brief Initialize an IRQ safe spinlock
*
* Initialize an IRQ safe spinlock. If the function returns successfully, the
* spinlock is in the unlocked state.
*
* This variant of spinlock is used for sharing a resource with an interrupt
* service routine. The IRQ safe variant should be used in this siutation to
* prevent deadlock between a thread/ISR that holds a spinlock while an interrupt
* occurs and the interrupt service routine trying to obtain the same spinlock
* too. If the spinlock is not used to share a resource with an interrupt service
* routine, one should use the osk_spinlock instead of the osk_spinlock_irq
* variant, see osk_spinlock_init().
* The caller must allocate the memory for the @see osk_spinlock_irq
* structure, which is then populated within this function. If the OS-specific
* spinlock referenced from the structure cannot be initialized, an error is
* returned.
*
* The spinlock must be terminated when no longer required, by using
* osk_spinlock_irq_term(). Otherwise, a resource leak may result in the OS.
*
* The spinlock is initialized with a lock order parameter, \a order. Refer to
* @see oskmutex_lockorder for more information on Rwlock/Mutex/Spinlock lock
* ordering.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to initialize a spinlock that is
* currently initialized.
*
* @param[out] lock pointer to a IRQ safe spinlock structure
* @param[in] order the locking order of the IRQ safe spinlock
* @return OSK_ERR_NONE on success, any other value indicates a failure.
*/
OSK_STATIC_INLINE osk_error osk_spinlock_irq_init(osk_spinlock_irq * const lock, osk_lock_order order) CHECK_RESULT;
/**
* @brief Terminate an IRQ safe spinlock
*
* Terminate the IRQ safe spinlock pointed to by \a lock, which must be
* a pointer to a valid unlocked IRQ safe spinlock. When the IRQ safe spinlock
* is terminated, the OS-specific spinlock is freed.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to terminate a IRQ safe pinlock that is
* currently terminated.
*
* @param[in] lock pointer to a valid IRQ safe spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_irq_term(osk_spinlock_irq * lock);
/**
* @brief Lock an IRQ safe spinlock
*
* Lock the IRQ safe spinlock (from here on refered to as 'spinlock') pointed to
* by \a lock. If the spinlock is currently unlocked, the calling thread returns
* with the spinlock locked. If a second thread attempts to lock the same spinlock,
* it polls the spinlock until the first thread unlocks the spinlock. If two or
* more threads are polling the spinlock waiting on the first thread to unlock the
* spinlock, it is undefined as to which thread will lock the spinlock when the
* first thread unlocks the spinlock.
*
* While the spinlock is locked by the calling thread, the spinlock implementation
* should prevent any possible deadlock issues arising from another thread on the
* same CPU trying to lock the same spinlock.
*
* While holding a spinlock, you must not sleep. You must not obtain a rwlock,
* mutex or do anything else that might block your thread. This is to prevent another
* thread trying to lock the same spinlock while your thread holds the spinlock,
* which could take a very long time (as it requires your thread to get scheduled
* in again and unlock the spinlock) or could even deadlock your system.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to lock a spinlock, rwlock or mutex with an order that
* is higher than any spinlock, rwlock, or mutex held by the current thread. Spinlocks,
* Rwlocks, and Mutexes must be locked in the order of highest to lowest, to prevent
* deadlocks. Refer to @see oskmutex_lockorder for more information.
*
* It is a programming error to exit a thread while it has a locked spinlock.
*
* @illegal It is illegal to call osk_spinlock_irq_lock() on a spinlock that is
* currently locked by the caller thread. That is, it is illegal for the same thread
* to lock a spinlock twice, without unlocking it in between.
*
* @param[in] lock pointer to a valid IRQ safe spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_irq_lock(osk_spinlock_irq * lock);
/**
* @brief Unlock an IRQ safe spinlock
*
* Unlock the IRQ safe spinlock (from hereon refered to as 'spinlock') pointed to
* by \a lock. The calling thread/ISR must be the same thread/ISR that locked the
* spinlock. If no other threads/ISRs are polling the spinlock waiting on the spinlock
* to be unlocked, the function returns* immediately, with the spinlock unlocked. If
* one or more threads/ISRs are polling the spinlock waiting on the spinlock to be unlocked,
* then this function returns, and a thread/ISR waiting on the spinlock can stop polling
* and continue with the spinlock locked. It is undefined as to which thread/ISR this is.
*
* @note It is not defined \em when a waiting thread/ISR continues. For example,
* a thread/ISR calling osk_spinlock_irq_unlock() followed by osk_spinlock_irq_lock() may
* (or may not) obtain the spinlock again, preventing other threads from continueing.
* Neither the 'immediately releasing', nor the 'delayed releasing'
* behavior of osk_spinlock_irq_unlock() can be relied upon. If such behavior is
* required, then you must implement it yourself, such as by using a second
* synchronization primitive.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* @illegal It is illegal for a thread to call osk_spinlock_irq_unlock() on a spinlock
* that it has not locked, even if that spinlock is currently locked by another
* thread. That is, it is illegal for any thread other than the 'owner' of the
* spinlock to unlock it. And, you must not unlock an already unlocked spinlock.
*
* @param[in] lock pointer to a valid IRQ safe spinlock structure
*/
OSK_STATIC_INLINE void osk_spinlock_irq_unlock(osk_spinlock_irq * lock);
/**
* @brief Initialize a rwlock
*
* Read/write locks allow multiple readers to obtain the lock (shared access),
* or one writer to obtain the lock (exclusive access).
* Read/write locks are created in an unlocked state.
*
* Initialize a rwlock structure. If the function returns successfully, the
* rwlock is in the unlocked state.
*
* The caller must allocate the memory for the @see osk_rwlock
* structure, which is then populated within this function. If the OS-specific
* rwlock referenced from the structure cannot be initialized, an error is
* returned.
*
* The rwlock must be terminated when no longer required, by using
* osk_rwlock_term(). Otherwise, a resource leak may result in the OS.
*
* The rwlock is initialized with a lock order parameter, \a order. Refer to
* @see oskmutex_lockorder for more information on Rwlock/Mutex/Spinlock lock
* ordering.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to initialize a rwlock that is
* currently initialized.
*
* @param[out] lock pointer to a rwlock structure
* @param[in] order the locking order of the rwlock
* @return OSK_ERR_NONE on success, any other value indicates a failure.
*/
OSK_STATIC_INLINE osk_error osk_rwlock_init(osk_rwlock * const lock, osk_lock_order order) CHECK_RESULT;
/**
* @brief Terminate a rwlock
*
* Terminate the rwlock pointed to by \a lock, which must be
* a pointer to a valid unlocked rwlock. When the rwlock is terminated, the
* OS-specific rwlock is freed.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to attempt to terminate a rwlock that is currently
* terminated.
*
* @illegal It is illegal to call osk_rwlock_term() on a locked rwlock.
*
* @param[in] lock pointer to a valid rwlock structure
*/
OSK_STATIC_INLINE void osk_rwlock_term(osk_rwlock * lock);
/**
* @brief Lock a rwlock for read access
*
* Lock the rwlock pointed to by \a lock for read access. A rwlock may
* be locked for read access by multiple threads. If the mutex
* mutex is not locked for exclusive write access, the calling thread
* returns with the rwlock locked for read access. If the mutex is
* currently locked for exclusive write access, the calling thread blocks
* until the thread with exclusive write access unlocks the rwlock.
* If multiple threads are blocked waiting for read access or exclusive
* write access to the rwlock, it is undefined as to which thread is
* unblocked when the rwlock is unlocked (by the thread with exclusive
* write access).
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to lock a rwlock, mutex or spinlock with an order that is
* higher than any rwlock, mutex or spinlock held by the current thread. Rwlocks, mutexes and
* spinlocks must be locked in the order of highest to lowest, to prevent
* deadlocks. Refer to @see oskmutex_lockorder for more information.
*
* It is a programming error to exit a thread while it has a locked rwlock.
*
* It is a programming error to lock a rwlock from an ISR context. In an ISR
* context you are not allowed to block what osk_rwlock_read_lock() potentially does.
*
* @illegal It is illegal to call osk_rwlock_read_lock() on a rwlock that is currently
* locked by the caller thread. That is, it is illegal for the same thread to
* lock a rwlock twice, without unlocking it in between.
* @param[in] lock pointer to a valid rwlock structure
*/
OSK_STATIC_INLINE void osk_rwlock_read_lock(osk_rwlock * lock);
/**
* @brief Unlock a rwlock for read access
*
* Unlock the rwlock pointed to by \a lock. The calling thread must be the
* same thread that locked the rwlock for read access. If no other threads
* are waiting on the rwlock to be unlocked, the function returns
* immediately, with the rwlock unlocked. If one or more threads are waiting
* on the rwlock to be unlocked for write access, and the calling thread
* is the last thread holding the rwlock for read access, then this function
* returns, and a thread waiting on the rwlock for write access can be
* unblocked. It is undefined as to which thread is unblocked.
*
* @note It is not defined \em when a waiting thread is unblocked. For example,
* a thread calling osk_rwlock_read_unlock() followed by osk_rwlock_read_lock()
* may (or may not) obtain the lock again, preventing other threads from being
* released. Neither the 'immediately releasing', nor the 'delayed releasing'
* behavior of osk_rwlock_read_unlock() can be relied upon. If such behavior is
* required, then you must implement it yourself, such as by using a second
* synchronization primitve.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* @illegal It is illegal for a thread to call osk_rwlock_read_unlock() on a
* rwlock that it has not locked, even if that rwlock is currently locked by another
* thread. That is, it is illegal for any thread other than the 'owner' of the
* rwlock to unlock it. And, you must not unlock an already unlocked rwlock.
* @param[in] lock pointer to a valid rwlock structure
*/
OSK_STATIC_INLINE void osk_rwlock_read_unlock(osk_rwlock * lock);
/**
* @brief Lock a rwlock for exclusive write access
*
* Lock the rwlock pointed to by \a lock for exclusive write access. If the
* rwlock is currently unlocked, the calling thread returns with the rwlock
* locked. If the rwlock is currently locked, the calling thread blocks
* until the last thread with read access or the thread with exclusive write
* access unlocks the rwlock. If multiple threads are blocked waiting
* for exclusive write access to the rwlock, it is undefined as to which
* thread is unblocked when the rwlock is unlocked (by either the last thread
* thread with read access or the thread with exclusive write access).
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* It is a programming error to lock a rwlock, mutex or spinlock with an order that is
* higher than any rwlock, mutex or spinlock held by the current thread. Rwlocks, mutexes and
* spinlocks must be locked in the order of highest to lowest, to prevent
* deadlocks. Refer to @see oskmutex_lockorder for more information.
*
* It is a programming error to exit a thread while it has a locked rwlock.
*
* It is a programming error to lock a rwlock from an ISR context. In an ISR
* context you are not allowed to block what osk_rwlock_write_lock() potentially does.
*
* @illegal It is illegal to call osk_rwlock_write_lock() on a rwlock that is currently
* locked by the caller thread. That is, it is illegal for the same thread to
* lock a rwlock twice, without unlocking it in between.
*
* @param[in] lock pointer to a valid rwlock structure
*/
OSK_STATIC_INLINE void osk_rwlock_write_lock(osk_rwlock * lock);
/**
* @brief Unlock a rwlock for exclusive write access
*
* Unlock the rwlock pointed to by \a lock. The calling thread must be the
* same thread that locked the rwlock for exclusive write access. If no
* other threads are waiting on the rwlock to be unlocked, the function returns
* immediately, with the rwlock unlocked. If one or more threads are waiting
* on the rwlock to be unlocked, then this function returns, and a thread
* waiting on the rwlock can be unblocked. It is undefined as to which
* thread is unblocked.
*
* @note It is not defined \em when a waiting thread is unblocked. For example,
* a thread calling osk_rwlock_write_unlock() followed by osk_rwlock_write_lock()
* may (or may not) obtain the lock again, preventing other threads from being
* released. Neither the 'immediately releasing', nor the 'delayed releasing'
* behavior of osk_rwlock_write_unlock() can be relied upon. If such behavior is
* required, then you must implement it yourself, such as by using a second
* synchronization primitve.
*
* It is a programming error to pass an invalid pointer (including NULL)
* through the \a lock parameter.
*
* @illegal It is illegal for a thread to call osk_rwlock_write_unlock() on a
* rwlock that it has not locked, even if that rwlock is currently locked by another
* thread. That is, it is illegal for any thread other than the 'owner' of the
* rwlock to unlock it. And, you must not unlock an already unlocked rwlock.
*
* @param[in] lock pointer to a valid read/write lock structure
*/
OSK_STATIC_INLINE void osk_rwlock_write_unlock(osk_rwlock * lock);
/* @} */ /* end group osklocks */
/** @} */ /* end group base_osk_api */
/** @} */ /* end group base_api */
/* pull in the arch header with the implementation */
#include <osk/mali_osk_arch_locks.h>
#ifdef __cplusplus
}
#endif
#endif /* _OSK_LOCKS_H_ */
|