KallistiOS git master
Independent SDK for the Sega Dreamcast
Loading...
Searching...
No Matches
rwsem.h
Go to the documentation of this file.
1/* KallistiOS ##version##
2
3 include/kos/rwsem.h
4 Copyright (C) 2008, 2010, 2012 Lawrence Sebald
5 Copyright (C) 2025 Paul Cercueil
6
7*/
8
9/** \file kos/rwsem.h
10 \brief Definition for a reader/writer semaphore.
11 \ingroup kthreads
12
13 This file defines a concept of reader/writer semaphores. Basically, this
14 type of lock allows an unlimited number of "readers" to acquire the lock at
15 a time, but only one "writer" (and only if no readers hold the lock).
16 Readers, by definition, should not change any global data (since they are
17 defined to only be reading), and since this is the case it is safe to allow
18 multiple readers to access global data that is shared amongst threads.
19 Writers on the other hand require exclusive access since they will be
20 changing global data in the critical section, and they cannot share with
21 a reader either (since the reader might attempt to read while the writer is
22 changing data).
23
24 This is implemented using two mutexes and an atomic read counter.
25 - The readers will lock the write mutex, then optionally the read mutex
26 (if they are the first reader), then unlock the write mutex. The writers
27 will lock the write mutex, then the read mutex.
28 - The write mutex is used to allow a pending writer to prevent new readers
29 from getting the semaphore.
30 - The read mutex is reserved when the first reader gets the semaphore, and
31 released after the last reader releases the lock. It is used to make the
32 writer wait until all readers are done.
33 - The atomic counter keeps count of the number of readers.
34
35 \author Lawrence Sebald
36 \author Paul Cercueil
37*/
38
39#ifndef __KOS_RWSEM_H
40#define __KOS_RWSEM_H
41
42#include <kos/cdefs.h>
43
44__BEGIN_DECLS
45
46#include <stddef.h>
47#include <kos/mutex.h>
48
49/** \brief Reader/writer semaphore structure.
50
51 All members of this structure should be considered to be private, it is not
52 safe to change anything in here yourself.
53
54 \headerfile kos/rwsem.h
55*/
56typedef struct rw_semaphore {
57 /** \brief The number of readers that are currently holding the lock. */
59
63
64/** \brief Initializer for a transient reader/writer semaphore */
65#define RWSEM_INITIALIZER { 0, MUTEX_INITIALIZER, MUTEX_INITIALIZER }
66
67/** \brief Initialize a reader/writer semaphore.
68
69 This function initializes a new reader/writer semaphore for use.
70
71 \retval 0 On success (no error conditions currently defined).
72*/
73int rwsem_init(rw_semaphore_t *s) __nonnull_all;
74
75/** \brief Destroy a reader/writer semaphore.
76
77 This function cleans up a reader/writer semaphore. It is an error to attempt
78 to destroy a r/w semaphore that is locked either for reading or writing.
79
80 \param s The r/w semaphore to destroy.
81 \retval 0 On success.
82 \retval -1 On error, errno will be set as appropriate.
83
84 \par Error Conditions:
85 \em EBUSY - the semaphore is still locked
86*/
87int rwsem_destroy(rw_semaphore_t *s) __nonnull_all;
88
89/** \brief Lock a reader/writer semaphore for reading (with a timeout).
90
91 This function attempts to lock the r/w semaphore for reading. If the
92 semaphore is locked for writing, this function will block until it is
93 possible to obtain the lock for reading or the timeout expires. This
94 function is <b>NOT</b> safe to call inside of an interrupt.
95
96 \param s The r/w semaphore to lock.
97 \param timeout The maximum time to wait (in milliseconds).
98 \retval 0 On success
99 \retval -1 On error, errno will be set as appropriate.
100
101 \par Error Conditions:
102 \em ETIMEDOUT - the timeout expires before the lock can be acquired \n
103*/
104int rwsem_read_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
105
106/** \brief Lock a reader/writer semaphore for reading.
107
108 This function attempts to lock the r/w semaphore for reading. If the
109 semaphore is locked for writing, this function will block until it is
110 possible to obtain the lock for reading. This function is <b>NOT</b> safe to
111 call inside of an interrupt; use rwsem_read_lock_irqsafe instead.
112
113 \param s The r/w semaphore to lock.
114 \retval 0 The return value is always 0.
115*/
116__nonnull_all
117static inline int rwsem_read_lock(rw_semaphore_t *s) {
118 return rwsem_read_lock_timed(s, 0);
119}
120
121/** \brief Lock a reader/writer semaphore for reading.
122
123 This function attempts to lock the r/w semaphore for reading. If the
124 semaphore is locked for writing, this function will block until it is
125 possible to obtain the lock for reading.
126 If called within an interrupt context, and the semaphore is already locked,
127 this function will return an error.
128
129 \param s The r/w semaphore to lock.
130 \retval 0 On success
131 \retval -1 On error, errno will be set as appropriate.
132
133 \par Error Conditions:
134 \em EWOULDBLOCK - called inside an interrupt and the semaphore was
135 already locked
136*/
138
139/** \brief Lock a reader/writer semaphore for writing (with a timeout).
140
141 This function attempts to lock the r/w semaphore for writing. If the
142 semaphore is locked for reading or writing, this function will block until
143 it is possible to obtain the lock for writing or the timeout expires. This
144 function is <b>NOT</b> safe to call inside of an interrupt.
145
146 \param s The r/w semaphore to lock.
147 \param timeout The maximum time to wait (in milliseconds).
148 \retval 0 On success.
149 \retval -1 On error, errno will be set as appropriate.
150
151 \par Error Conditions:
152 \em ETIMEDOUT - the timeout expires before the lock can be acquired \n
153*/
154int rwsem_write_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
155
156/** \brief Lock a reader/writer semaphore for writing.
157
158 This function attempts to lock the r/w semaphore for writing. If the
159 semaphore is locked for reading or writing, this function will block until
160 it is possible to obtain the lock for writing. This function is <b>NOT</b>
161 safe to call inside of an interrupt; use rwsem_write_lock_irqsafe instead.
162
163 \param s The r/w semaphore to lock.
164 \retval 0 The return value is always 0.
165*/
166__nonnull_all
167static inline int rwsem_write_lock(rw_semaphore_t *s) {
168 return rwsem_write_lock_timed(s, 0);
169}
170
171/** \brief Lock a reader/writer semaphore for writing.
172
173 This function attempts to lock the r/w semaphore for writing. If the
174 semaphore is locked for reading or writing, this function will block until
175 it is possible to obtain the lock for writing.
176 If called within an interrupt context, and the semaphore is already locked,
177 this function will return an error.
178
179 \param s The r/w semaphore to lock.
180 \retval 0 On success.
181 \retval -1 On error, errno will be set as appropriate.
182
183 \par Error conditions:
184 \em EWOULDBLOCK - called inside an interrupt and the semaphore was
185 already locked
186*/
188
189/** \brief Unlock a reader/writer semaphore from a read lock.
190
191 This function releases one instance of the read lock on the r/w semaphore.
192
193 \param s The r/w semaphore to release the read lock on.
194 \retval 0 On success.
195 \retval -1 On error, errno will be set as appropriate.
196
197 \par Error Conditions:
198 \em EPERM - the read lock is not currently held \n
199*/
201
202/** \brief Unlock a reader/writer semaphore from a write lock.
203
204 This function releases one instance of the write lock on the r/w semaphore.
205
206 \param s The r/w semaphore to release the write lock on.
207 \retval 0 On success.
208 \retval -1 On error, errno will be set as appropriate.
209
210 \par Error Conditions:
211 \em EPERM - the write lock is not currently held by the calling
212 thread \n
213*/
215
216/** \brief Unlock a reader/writer semaphore.
217
218 This function releases the lock held by the current thread on the specified
219 reader/writer semaphore. This function will automatically determine which
220 lock is held by the calling thread and release it as appropriate.
221
222 This function is <b>NOT</b> safe to call (in general) if you do not hold the
223 lock!
224
225 \param s The r/w semaphore to release the lock on.
226 \retval 0 On success.
227 \retval -1 On error, errno will be set as appropriate.
228
229 \par Error Conditions:
230 \em EPERM - the lock is not currently held by the calling thread \n
231*/
232int rwsem_unlock(rw_semaphore_t *s) __nonnull_all;
233
234/** \brief Attempt to lock a reader/writer semaphore for reading.
235
236 This function attempts to lock the r/w semaphore for reading. If for any
237 reason rwsem_read_lock would normally block, this function will return an
238 error. This function is safe to call inside an interrupt.
239
240 \param s The r/w semaphore to attempt to lock.
241 \retval 0 On success.
242 \retval -1 On error, errno will be set as appropriate.
243
244 \par Error Conditions:
245 \em EWOULDBLOCK - a call to rwsem_read_lock would block \n
246*/
248
249/** \brief Attempt to lock a reader/writer semaphore for writing.
250
251 This function attempts to lock the r/w semaphore for writing. If for any
252 reason rwsem_write_lock would normally block, this function will return an
253 error. This function is safe to call inside an interrupt.
254
255 \param s The r/w semaphore to attempt to lock.
256 \retval 0 On success.
257 \retval -1 On error, errno will be set as appropriate.
258
259 \par Error Conditions:
260 \em EWOULDBLOCK - a call to rwsem_write_lock would block \n
261*/
263
264/** \brief Upgrade a thread from reader status to writer status (with a
265 timeout).
266
267 This function will upgrade the lock on the calling thread from a reader
268 state to a writer state. If it cannot do this at the moment, it will block
269 until it is possible. This function is <b>NOT</b> safe to call inside an
270 interrupt.
271
272 You can only have one reader waiting to upgrade at a time, otherwise the
273 state would potentially become corrupted between when this is called and
274 when you get the lock. If you get -1 back from this, you must not assume
275 that you can write safely! On error, the calling thread will still hold a
276 read lock.
277
278 \param s The r/w semaphore to upgrade.
279 \param timeout The maximum time to wait (in milliseconds).
280 \retval 0 On success.
281 \retval -1 On error, errno will be set as appropriate.
282
283 \par Error Conditions:
284 \em EBUSY - another reader has already requested an upgrade \n
285 \em ETIMEDOUT - the timeout expired before the write lock could be
286 acquired
287*/
288int rwsem_read_upgrade_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
289
290/** \brief Upgrade a thread from reader status to writer status.
291
292 This function will upgrade the lock on the calling thread from a reader
293 state to a writer state. If it cannot do this at the moment, it will block
294 until it is possible. This function is <b>NOT</b> safe to call inside an
295 interrupt.
296
297 You can only have one reader waiting to upgrade at a time, otherwise the
298 state would potentially become corrupted between when this is called and
299 when you get the lock. If you get -1 back from this, you must not assume
300 that you can write safely! On error, the calling thread will still hold a
301 read lock.
302
303 \param s The r/w semaphore to upgrade.
304 \retval 0 On success.
305 \retval -1 On error, errno will be set as appropriate.
306
307 \par Error Conditions:
308 \em EBUSY - another reader has already requested an upgrade
309*/
310__nonnull_all
312 return rwsem_read_upgrade_timed(s, 0);
313}
314
315/** \brief Attempt to upgrade a thread from reader status to writer status.
316
317 This function will attempt to upgrade the lock on the calling thread to
318 writer status. If for any reason rwsem_read_upgrade would block, this
319 function will return an error. This function is safe to call inside an
320 interrupt. Note that on error, the read lock is still held!
321
322 \param s The r/w semaphore to upgrade.
323 \retval 0 On success.
324 \retval -1 On error, errno will be set as appropriate.
325
326 \par Error Conditions:
327 \em EWOULDBLOCK - a call to rwsem_read_upgrade would block \n
328 \em EBUSY - another reader has already requested an upgrade \n
329*/
331
332/** \brief Read the reader count on the reader/writer semaphore.
333
334 This function is not a safe way to see if the lock will be locked by any
335 readers when you get around to locking it, so do not use it in this way.
336
337 \param s The r/w semaphore to count the readers on.
338 \return The number of readers holding the r/w semaphore.
339*/
340int rwsem_read_count(const rw_semaphore_t *s) __nonnull_all;
341
342/** \brief Read the state of the writer lock on the reader/writer semaphore.
343
344 This function is not a safe way to see if the lock will be locked by a
345 writer by the time you get around to doing something with it, so don't try
346 to use it for that purpose.
347
348 \param s The r/w semaphore to check the writer status on.
349 \return The status of the writer lock of the r/w semaphore.
350*/
351int rwsem_write_locked(const rw_semaphore_t *s) __nonnull_all;
352
353__END_DECLS
354
355#endif /* __KOS_RWSEM_H */
Various common macros used throughout the codebase.
Mutual exclusion locks.
static __nonnull_all int rwsem_read_lock(rw_semaphore_t *s)
Lock a reader/writer semaphore for reading.
Definition rwsem.h:117
int rwsem_write_unlock(rw_semaphore_t *s) __nonnull_all
Unlock a reader/writer semaphore from a write lock.
int rwsem_read_trylock(rw_semaphore_t *s) __nonnull_all
Attempt to lock a reader/writer semaphore for reading.
static __nonnull_all int rwsem_read_upgrade(rw_semaphore_t *s)
Upgrade a thread from reader status to writer status.
Definition rwsem.h:311
int rwsem_unlock(rw_semaphore_t *s) __nonnull_all
Unlock a reader/writer semaphore.
int rwsem_read_upgrade_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all
Upgrade a thread from reader status to writer status (with a timeout).
int rwsem_write_trylock(rw_semaphore_t *s) __nonnull_all
Attempt to lock a reader/writer semaphore for writing.
int rwsem_read_tryupgrade(rw_semaphore_t *s) __nonnull_all
Attempt to upgrade a thread from reader status to writer status.
static __nonnull_all int rwsem_write_lock(rw_semaphore_t *s)
Lock a reader/writer semaphore for writing.
Definition rwsem.h:167
int rwsem_write_lock_irqsafe(rw_semaphore_t *s) __nonnull_all
Lock a reader/writer semaphore for writing.
int rwsem_read_unlock(rw_semaphore_t *s) __nonnull_all
Unlock a reader/writer semaphore from a read lock.
int rwsem_read_count(const rw_semaphore_t *s) __nonnull_all
Read the reader count on the reader/writer semaphore.
int rwsem_read_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all
Lock a reader/writer semaphore for reading (with a timeout).
int rwsem_read_lock_irqsafe(rw_semaphore_t *s) __nonnull_all
Lock a reader/writer semaphore for reading.
int rwsem_write_locked(const rw_semaphore_t *s) __nonnull_all
Read the state of the writer lock on the reader/writer semaphore.
int rwsem_init(rw_semaphore_t *s) __nonnull_all
Initialize a reader/writer semaphore.
int rwsem_destroy(rw_semaphore_t *s) __nonnull_all
Destroy a reader/writer semaphore.
int rwsem_write_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all
Lock a reader/writer semaphore for writing (with a timeout).
rw_semaphore_t s
Definition rwsem_test.c:25
Mutual exclusion lock type.
Definition mutex.h:56
Reader/writer semaphore structure.
Definition rwsem.h:56
int read_count
The number of readers that are currently holding the lock.
Definition rwsem.h:58
mutex_t read_lock
Definition rwsem.h:61
mutex_t write_lock
Definition rwsem.h:60