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#include <kos/sem.h>
49
50/** \brief Reader/writer semaphore structure.
51
52 All members of this structure should be considered to be private, it is not
53 safe to change anything in here yourself.
54
55 \headerfile kos/rwsem.h
56*/
57typedef struct rw_semaphore {
58 /** \brief The number of readers that are currently holding the lock. */
60
64
65/** \brief Initializer for a transient reader/writer semaphore */
66#define RWSEM_INITIALIZER { 0, MUTEX_INITIALIZER, SEM_INITIALIZER(1) }
67
68/** \brief Initialize a reader/writer semaphore.
69
70 This function initializes a new reader/writer semaphore for use.
71
72 \retval 0 On success (no error conditions currently defined).
73*/
74int rwsem_init(rw_semaphore_t *s) __nonnull_all;
75
76/** \brief Destroy a reader/writer semaphore.
77
78 This function cleans up a reader/writer semaphore. It is an error to attempt
79 to destroy a r/w semaphore that is locked either for reading or writing.
80
81 \param s The r/w semaphore to destroy.
82 \retval 0 On success.
83 \retval -1 On error, errno will be set as appropriate.
84
85 \par Error Conditions:
86 \em EBUSY - the semaphore is still locked
87*/
88int rwsem_destroy(rw_semaphore_t *s) __nonnull_all;
89
90/** \brief Lock a reader/writer semaphore for reading (with a timeout).
91
92 This function attempts to lock the r/w semaphore for reading. If the
93 semaphore is locked for writing, this function will block until it is
94 possible to obtain the lock for reading or the timeout expires. This
95 function is <b>NOT</b> safe to call inside of an interrupt.
96
97 \param s The r/w semaphore to lock.
98 \param timeout The maximum time to wait (in milliseconds).
99 \retval 0 On success
100 \retval -1 On error, errno will be set as appropriate.
101
102 \par Error Conditions:
103 \em ETIMEDOUT - the timeout expires before the lock can be acquired \n
104*/
105int rwsem_read_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
106
107/** \brief Lock a reader/writer semaphore for reading.
108
109 This function attempts to lock the r/w semaphore for reading. If the
110 semaphore is locked for writing, this function will block until it is
111 possible to obtain the lock for reading. This function is <b>NOT</b> safe to
112 call inside of an interrupt; use rwsem_read_lock_irqsafe instead.
113
114 \param s The r/w semaphore to lock.
115 \retval 0 The return value is always 0.
116*/
117__nonnull_all
118static inline int rwsem_read_lock(rw_semaphore_t *s) {
119 return rwsem_read_lock_timed(s, 0);
120}
121
122/** \brief Lock a reader/writer semaphore for reading.
123
124 This function attempts to lock the r/w semaphore for reading. If the
125 semaphore is locked for writing, this function will block until it is
126 possible to obtain the lock for reading.
127 If called within an interrupt context, and the semaphore is already locked,
128 this function will return an error.
129
130 \param s The r/w semaphore to lock.
131 \retval 0 On success
132 \retval -1 On error, errno will be set as appropriate.
133
134 \par Error Conditions:
135 \em EWOULDBLOCK - called inside an interrupt and the semaphore was
136 already locked
137*/
139
140/** \brief Lock a reader/writer semaphore for writing (with a timeout).
141
142 This function attempts to lock the r/w semaphore for writing. If the
143 semaphore is locked for reading or writing, this function will block until
144 it is possible to obtain the lock for writing or the timeout expires. This
145 function is <b>NOT</b> safe to call inside of an interrupt.
146
147 \param s The r/w semaphore to lock.
148 \param timeout The maximum time to wait (in milliseconds).
149 \retval 0 On success.
150 \retval -1 On error, errno will be set as appropriate.
151
152 \par Error Conditions:
153 \em ETIMEDOUT - the timeout expires before the lock can be acquired \n
154*/
155int rwsem_write_lock_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
156
157/** \brief Lock a reader/writer semaphore for writing.
158
159 This function attempts to lock the r/w semaphore for writing. If the
160 semaphore is locked for reading or writing, this function will block until
161 it is possible to obtain the lock for writing. This function is <b>NOT</b>
162 safe to call inside of an interrupt; use rwsem_write_lock_irqsafe instead.
163
164 \param s The r/w semaphore to lock.
165 \retval 0 The return value is always 0.
166*/
167__nonnull_all
168static inline int rwsem_write_lock(rw_semaphore_t *s) {
169 return rwsem_write_lock_timed(s, 0);
170}
171
172/** \brief Lock a reader/writer semaphore for writing.
173
174 This function attempts to lock the r/w semaphore for writing. If the
175 semaphore is locked for reading or writing, this function will block until
176 it is possible to obtain the lock for writing.
177 If called within an interrupt context, and the semaphore is already locked,
178 this function will return an error.
179
180 \param s The r/w semaphore to lock.
181 \retval 0 On success.
182 \retval -1 On error, errno will be set as appropriate.
183
184 \par Error conditions:
185 \em EWOULDBLOCK - called inside an interrupt and the semaphore was
186 already locked
187*/
189
190/** \brief Unlock a reader/writer semaphore from a read lock.
191
192 This function releases one instance of the read lock on the r/w semaphore.
193
194 \param s The r/w semaphore to release the read lock on.
195 \retval 0 On success.
196 \retval -1 On error, errno will be set as appropriate.
197
198 \par Error Conditions:
199 \em EPERM - the read lock is not currently held \n
200*/
202
203/** \brief Unlock a reader/writer semaphore from a write lock.
204
205 This function releases one instance of the write lock on the r/w semaphore.
206
207 \param s The r/w semaphore to release the write lock on.
208 \retval 0 On success.
209 \retval -1 On error, errno will be set as appropriate.
210
211 \par Error Conditions:
212 \em EPERM - the write lock is not currently held by the calling
213 thread \n
214*/
216
217/** \brief Unlock a reader/writer semaphore.
218
219 This function releases the lock held by the current thread on the specified
220 reader/writer semaphore. This function will automatically determine which
221 lock is held by the calling thread and release it as appropriate.
222
223 This function is <b>NOT</b> safe to call (in general) if you do not hold the
224 lock!
225
226 \param s The r/w semaphore to release the lock on.
227 \retval 0 On success.
228 \retval -1 On error, errno will be set as appropriate.
229
230 \par Error Conditions:
231 \em EPERM - the lock is not currently held by the calling thread \n
232*/
233int rwsem_unlock(rw_semaphore_t *s) __nonnull_all;
234
235/** \brief Attempt to lock a reader/writer semaphore for reading.
236
237 This function attempts to lock the r/w semaphore for reading. If for any
238 reason rwsem_read_lock would normally block, this function will return an
239 error. This function is safe to call inside an interrupt.
240
241 \param s The r/w semaphore to attempt to lock.
242 \retval 0 On success.
243 \retval -1 On error, errno will be set as appropriate.
244
245 \par Error Conditions:
246 \em EWOULDBLOCK - a call to rwsem_read_lock would block \n
247*/
249
250/** \brief Attempt to lock a reader/writer semaphore for writing.
251
252 This function attempts to lock the r/w semaphore for writing. If for any
253 reason rwsem_write_lock would normally block, this function will return an
254 error. This function is safe to call inside an interrupt.
255
256 \param s The r/w semaphore to attempt to lock.
257 \retval 0 On success.
258 \retval -1 On error, errno will be set as appropriate.
259
260 \par Error Conditions:
261 \em EWOULDBLOCK - a call to rwsem_write_lock would block \n
262*/
264
265/** \brief Upgrade a thread from reader status to writer status (with a
266 timeout).
267
268 This function will upgrade the lock on the calling thread from a reader
269 state to a writer state. If it cannot do this at the moment, it will block
270 until it is possible. This function is <b>NOT</b> safe to call inside an
271 interrupt.
272
273 You can only have one reader waiting to upgrade at a time, otherwise the
274 state would potentially become corrupted between when this is called and
275 when you get the lock. If you get -1 back from this, you must not assume
276 that you can write safely! On error, the calling thread will still hold a
277 read lock.
278
279 \param s The r/w semaphore to upgrade.
280 \param timeout The maximum time to wait (in milliseconds).
281 \retval 0 On success.
282 \retval -1 On error, errno will be set as appropriate.
283
284 \par Error Conditions:
285 \em EBUSY - another reader has already requested an upgrade \n
286 \em ETIMEDOUT - the timeout expired before the write lock could be
287 acquired
288*/
289int rwsem_read_upgrade_timed(rw_semaphore_t *s, unsigned int timeout) __nonnull_all;
290
291/** \brief Upgrade a thread from reader status to writer status.
292
293 This function will upgrade the lock on the calling thread from a reader
294 state to a writer state. If it cannot do this at the moment, it will block
295 until it is possible. This function is <b>NOT</b> safe to call inside an
296 interrupt.
297
298 You can only have one reader waiting to upgrade at a time, otherwise the
299 state would potentially become corrupted between when this is called and
300 when you get the lock. If you get -1 back from this, you must not assume
301 that you can write safely! On error, the calling thread will still hold a
302 read lock.
303
304 \param s The r/w semaphore to upgrade.
305 \retval 0 On success.
306 \retval -1 On error, errno will be set as appropriate.
307
308 \par Error Conditions:
309 \em EBUSY - another reader has already requested an upgrade
310*/
311__nonnull_all
313 return rwsem_read_upgrade_timed(s, 0);
314}
315
316/** \brief Attempt to upgrade a thread from reader status to writer status.
317
318 This function will attempt to upgrade the lock on the calling thread to
319 writer status. If for any reason rwsem_read_upgrade would block, this
320 function will return an error. This function is safe to call inside an
321 interrupt. Note that on error, the read lock is still held!
322
323 \param s The r/w semaphore to upgrade.
324 \retval 0 On success.
325 \retval -1 On error, errno will be set as appropriate.
326
327 \par Error Conditions:
328 \em EWOULDBLOCK - a call to rwsem_read_upgrade would block \n
329 \em EBUSY - another reader has already requested an upgrade \n
330*/
332
333/** \brief Read the reader count on the reader/writer semaphore.
334
335 This function is not a safe way to see if the lock will be locked by any
336 readers when you get around to locking it, so do not use it in this way.
337
338 \param s The r/w semaphore to count the readers on.
339 \return The number of readers holding the r/w semaphore.
340*/
341int rwsem_read_count(const rw_semaphore_t *s) __nonnull_all;
342
343/** \brief Read the state of the writer lock on the reader/writer semaphore.
344
345 This function is not a safe way to see if the lock will be locked by a
346 writer by the time you get around to doing something with it, so don't try
347 to use it for that purpose.
348
349 \param s The r/w semaphore to check the writer status on.
350 \return The status of the writer lock of the r/w semaphore.
351*/
352int rwsem_write_locked(const rw_semaphore_t *s) __nonnull_all;
353
354__END_DECLS
355
356#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:118
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:312
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:168
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
Semaphores.
Mutual exclusion lock type.
Definition mutex.h:56
Reader/writer semaphore structure.
Definition rwsem.h:57
int read_count
The number of readers that are currently holding the lock.
Definition rwsem.h:59
mutex_t write_lock
Definition rwsem.h:61
semaphore_t read_sem
Definition rwsem.h:62
Semaphore type.
Definition sem.h:37