1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2010-10-26 Bernard the first version
9 */
10
11 #include <pthread.h>
12
pthread_rwlockattr_init(pthread_rwlockattr_t * attr)13 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
14 {
15 if (!attr)
16 return EINVAL;
17 *attr = PTHREAD_PROCESS_PRIVATE;
18
19 return 0;
20 }
21 RTM_EXPORT(pthread_rwlockattr_init);
22
pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr)23 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
24 {
25 if (!attr)
26 return EINVAL;
27
28 return 0;
29 }
30 RTM_EXPORT(pthread_rwlockattr_destroy);
31
pthread_rwlockattr_getpshared(const pthread_rwlockattr_t * attr,int * pshared)32 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
33 int *pshared)
34 {
35 if (!attr || !pshared)
36 return EINVAL;
37
38 *pshared = PTHREAD_PROCESS_PRIVATE;
39
40 return 0;
41 }
42 RTM_EXPORT(pthread_rwlockattr_getpshared);
43
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * attr,int pshared)44 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
45 {
46 if (!attr || pshared != PTHREAD_PROCESS_PRIVATE)
47 return EINVAL;
48
49 return 0;
50 }
51 RTM_EXPORT(pthread_rwlockattr_setpshared);
52
53 /**
54 * @brief Initializes a read-write lock.
55 *
56 * This function initializes the read-write lock object pointed to by `rwlock` with the
57 * attributes specified by `attr`. If `attr` is `NULL`, the default attributes are used.
58 * A read-write lock allows multiple threads to read or a single thread to write, but not both simultaneously.
59 *
60 * @param rwlock A pointer to the read-write lock object to be initialized.
61 * Must point to valid memory.
62 * @param attr A pointer to the attributes for the read-write lock.
63 * If `NULL`, default attributes are applied.
64 *
65 * @return
66 * - `0` on success.
67 * - A non-zero error code on failure, including:
68 * - `EINVAL`: Invalid attributes.
69 *
70 * @note
71 * - The read-write lock must be destroyed using `pthread_rwlock_destroy()` when it is no longer needed.
72 * - 'rw_mutex' is used for protecting rwlock data.
73 * 'rw_condreaders' is a condition variable for controlling readers.
74 * 'rw_condwriters' is a condition variable for controlling writers.
75 *
76 * @see pthread_rwlock_destroy, pthread_rwlock_rdlock, pthread_rwlock_wrlock, pthread_rwlock_unlock
77 */
pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr)78 int pthread_rwlock_init(pthread_rwlock_t *rwlock,
79 const pthread_rwlockattr_t *attr)
80 {
81 if (!rwlock)
82 return EINVAL;
83
84 rwlock->attr = PTHREAD_PROCESS_PRIVATE;
85 pthread_mutex_init(&(rwlock->rw_mutex), NULL);
86 pthread_cond_init(&(rwlock->rw_condreaders), NULL);
87 pthread_cond_init(&(rwlock->rw_condwriters), NULL);
88
89 rwlock->rw_nwaitwriters = 0;
90 rwlock->rw_nwaitreaders = 0;
91 rwlock->rw_refcount = 0;
92
93 return 0;
94 }
95 RTM_EXPORT(pthread_rwlock_init);
96
97 /**
98 * @brief Destroys a read-write lock.
99 *
100 * This function destroys the read-write lock object pointed to by `rwlock`. After
101 * the lock is destroyed, it cannot be used until it is reinitialized with
102 * `pthread_rwlock_init`. Any threads currently blocked on the lock are affected by the destruction.
103 *
104 * @param rwlock A pointer to the read-write lock object to be destroyed.
105 * Must point to a valid, initialized read-write lock.
106 *
107 * @return
108 * - `0` on success.
109 * - A non-zero error code on failure, including:
110 * - `EINVAL`: The `rwlock` is invalid or uninitialized.
111 * - `EBUSY`: The lock is currently in use by a thread, and cannot be destroyed.
112 *
113 * @note
114 * - The read-write lock must not be in use (i.e., no threads should be blocked on it)
115 * when `pthread_rwlock_destroy` is called.
116 * - Calling this function on an uninitialized or destroyed lock will result in undefined behavior.
117 * - Ensure that all threads have unlocked the lock before attempting to destroy it.
118 *
119 * @see pthread_rwlock_init, pthread_rwlock_rdlock, pthread_rwlock_wrlock, pthread_rwlock_unlock
120 */
pthread_rwlock_destroy(pthread_rwlock_t * rwlock)121 int pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
122 {
123 int result;
124
125 if (!rwlock)
126 return EINVAL;
127 if (rwlock->attr == -1)
128 return 0; /* rwlock is not initialized */
129
130 if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
131 return(result);
132
133 if (rwlock->rw_refcount != 0 ||
134 rwlock->rw_nwaitreaders != 0 ||
135 rwlock->rw_nwaitwriters != 0)
136 {
137 result = EBUSY;
138
139 return result;
140 }
141 else
142 {
143 /* check whether busy */
144 result = rt_sem_trytake(&(rwlock->rw_condreaders.sem));
145 if (result == RT_EOK)
146 {
147 result = rt_sem_trytake(&(rwlock->rw_condwriters.sem));
148 if (result == RT_EOK)
149 {
150 rt_sem_release(&(rwlock->rw_condreaders.sem));
151 rt_sem_release(&(rwlock->rw_condwriters.sem));
152
153 pthread_cond_destroy(&rwlock->rw_condreaders);
154 pthread_cond_destroy(&rwlock->rw_condwriters);
155 }
156 else
157 {
158 rt_sem_release(&(rwlock->rw_condreaders.sem));
159 result = EBUSY;
160 }
161 }
162 else
163 result = EBUSY;
164 }
165
166 pthread_mutex_unlock(&rwlock->rw_mutex);
167 if (result == 0)
168 pthread_mutex_destroy(&rwlock->rw_mutex);
169
170 return result;
171 }
172 RTM_EXPORT(pthread_rwlock_destroy);
173
174 /**
175 * @brief Acquire a read lock on a read-write lock.
176 *
177 * This function locks the specified read-write lock for reading. If the lock
178 * is already held by one or more threads for reading, the calling thread
179 * can acquire the lock as well (shared access). However, if the lock is
180 * held by another writer thread, or other writer thread has been waiting
181 * for the lock, the calling thread will block until the write lock is released.
182 *
183 * @param rwlock A pointer to the read-write lock to be locked.
184 *
185 * @return - 0 on success.
186 * - EINVAL if the rwlock is invalid.
187 * - EDEADLK if a deadlock condition is detected (optional; implementation-dependent).
188 *
189 * @note A thread that has acquired a read lock must eventually release it
190 * using `pthread_rwlock_unlock`. Multiple read locks can be held
191 * simultaneously, but a write lock excludes all other locks.
192 *
193 * @see pthread_rwlock_unlock
194 * @see pthread_rwlock_wrlock
195 * @see pthread_rwlock_tryrdlock
196 */
pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)197 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
198 {
199 int result;
200
201 if (!rwlock)
202 return EINVAL;
203 if (rwlock->attr == -1)
204 pthread_rwlock_init(rwlock, NULL);
205
206 if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
207 return(result);
208
209 /* give preference to waiting writers */
210 while (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0)
211 {
212 rwlock->rw_nwaitreaders++;
213 /* rw_mutex will be released when waiting for rw_condreaders */
214 result = pthread_cond_wait(&rwlock->rw_condreaders, &rwlock->rw_mutex);
215 /* rw_mutex should have been taken again when returned from waiting */
216 rwlock->rw_nwaitreaders--;
217 if (result != 0) /* wait error */
218 break;
219 }
220
221 /* another reader has a read lock */
222 if (result == 0)
223 rwlock->rw_refcount++;
224
225 pthread_mutex_unlock(&rwlock->rw_mutex);
226
227 return (result);
228 }
229 RTM_EXPORT(pthread_rwlock_rdlock);
230
231 /**
232 * @brief Try to acquire a read lock on a read-write lock without blocking.
233 *
234 * This function attempts to acquire a read lock on the specified read-write lock.
235 * If the lock is already held for writing, the function will return immediately
236 * without blocking the calling thread. If the lock is available for reading,
237 * it will be acquired successfully.
238 *
239 * @param rwlock A pointer to the read-write lock to attempt to lock.
240 *
241 * @return - 0 on success, indicating the read lock was acquired.
242 * - EBUSY if the lock is currently held for writing by another thread.
243 * - EINVAL if the rwlock is invalid.
244 *
245 * @note This function is non-blocking and returns immediately if the lock
246 * cannot be acquired. After successfully acquiring the read lock,
247 * the thread must release it using `pthread_rwlock_unlock`.
248 *
249 * @see pthread_rwlock_unlock
250 * @see pthread_rwlock_rdlock
251 * @see pthread_rwlock_trywrlock
252 */
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)253 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
254 {
255 int result;
256
257 if (!rwlock)
258 return EINVAL;
259 if (rwlock->attr == -1)
260 pthread_rwlock_init(rwlock, NULL);
261
262 if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
263 return(result);
264
265 if (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0)
266 result = EBUSY; /* held by a writer or waiting writers */
267 else
268 rwlock->rw_refcount++; /* increment count of reader locks */
269
270 pthread_mutex_unlock(&rwlock->rw_mutex);
271
272 return(result);
273 }
274 RTM_EXPORT(pthread_rwlock_tryrdlock);
275
276 /**
277 * @brief Acquire a read lock on a read-write lock with a timeout.
278 *
279 * This function attempts to lock the specified read-write lock for reading,
280 * blocking until the lock becomes available or the specified timeout expires.
281 * If the lock is held for writing by another thread, the calling thread will
282 * block, but only up to the time specified by `abstime`.
283 *
284 * @param rwlock A pointer to the read-write lock to be locked.
285 * @param abstime A pointer to a `timespec` structure specifying the
286 * absolute timeout (in seconds and nanoseconds since the
287 * Epoch, 1970-01-01 00:00:00 UTC).
288 *
289 * @return - 0 on success, indicating the read lock was acquired.
290 * - ETIMEDOUT if the timeout expired before the lock could be acquired.
291 * - EINVAL if the `rwlock` is invalid or `abstime` contains invalid values.
292 * - EDEADLK if a deadlock condition is detected (optional; implementation-dependent).
293 *
294 * @note The timeout is specified as an absolute time (not relative). After
295 * acquiring the read lock, the thread must release it using
296 * `pthread_rwlock_unlock`.
297 *
298 * @warning If the system clock is changed (e.g., via manual adjustment or
299 * NTP synchronization), the timeout behavior may be affected.
300 *
301 * @see pthread_rwlock_unlock
302 * @see pthread_rwlock_rdlock
303 * @see pthread_rwlock_tryrdlock
304 */
pthread_rwlock_timedrdlock(pthread_rwlock_t * rwlock,const struct timespec * abstime)305 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
306 const struct timespec *abstime)
307 {
308 int result;
309
310 if (!rwlock)
311 return EINVAL;
312 if (rwlock->attr == -1)
313 pthread_rwlock_init(rwlock, NULL);
314
315 if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
316 return(result);
317
318 /* give preference to waiting writers */
319 while (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0)
320 {
321 rwlock->rw_nwaitreaders++;
322 /* rw_mutex will be released when waiting for rw_condreaders */
323 result = pthread_cond_timedwait(&rwlock->rw_condreaders, &rwlock->rw_mutex, abstime);
324 /* rw_mutex should have been taken again when returned from waiting */
325 rwlock->rw_nwaitreaders--;
326 if (result != 0)
327 break;
328 }
329
330 /* another reader has a read lock */
331 if (result == 0)
332 rwlock->rw_refcount++;
333
334 pthread_mutex_unlock(&rwlock->rw_mutex);
335
336 return (result);
337 }
338 RTM_EXPORT(pthread_rwlock_timedrdlock);
339
340 /**
341 * @brief Acquire a write lock on a read-write lock with a timeout.
342 *
343 * This function attempts to acquire a write lock on the specified read-write lock,
344 * blocking until the lock becomes available or the specified timeout expires.
345 * If the lock is already held for reading or writing by another thread, the
346 * calling thread will block, but only up to the time specified by `abstime`.
347 *
348 * @param rwlock A pointer to the read-write lock to be locked.
349 * @param abstime A pointer to a `timespec` structure specifying the
350 * absolute timeout (in seconds and nanoseconds since the
351 * Epoch, 1970-01-01 00:00:00 UTC).
352 *
353 * @return
354 * - 0 on success, indicating the write lock was acquired.
355 * - EINVAL if the `rwlock` is invalid or `abstime` contains invalid values.
356 * - EDEADLK if a deadlock condition is detected (optional; implementation-dependent).
357 *
358 * @note The timeout is specified as an absolute time (not relative). After
359 * acquiring the write lock, the thread must release it using
360 * `pthread_rwlock_unlock`.
361 *
362 * @warning If the system clock is adjusted (e.g., manually or via NTP synchronization),
363 * the timeout behavior may be affected.
364 *
365 * @see pthread_rwlock_unlock
366 * @see pthread_rwlock_wrlock
367 * @see pthread_rwlock_trywrlock
368 */
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlock,const struct timespec * abstime)369 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
370 const struct timespec *abstime)
371 {
372 int result;
373
374 if (!rwlock)
375 return EINVAL;
376 if (rwlock->attr == -1)
377 pthread_rwlock_init(rwlock, NULL);
378
379 if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
380 return(result);
381
382 while (rwlock->rw_refcount != 0)
383 {
384 rwlock->rw_nwaitwriters++;
385 /* rw_mutex will be released when waiting for rw_condwriters */
386 result = pthread_cond_timedwait(&rwlock->rw_condwriters, &rwlock->rw_mutex, abstime);
387 /* rw_mutex should have been taken again when returned from waiting */
388 rwlock->rw_nwaitwriters--;
389
390 if (result != 0)
391 break;
392 }
393
394 if (result == 0)
395 rwlock->rw_refcount = -1;
396
397 pthread_mutex_unlock(&rwlock->rw_mutex);
398
399 return(result);
400 }
401 RTM_EXPORT(pthread_rwlock_timedwrlock);
402
403 /**
404 * @brief Try to acquire a write lock on a read-write lock without blocking.
405 *
406 * This function attempts to acquire a write lock on the specified read-write lock.
407 * If the lock is already held for reading or writing by another thread, the function
408 * will return immediately without blocking the calling thread. If the lock is
409 * available, it will be acquired successfully.
410 *
411 * @param rwlock A pointer to the read-write lock to attempt to lock.
412 *
413 * @return
414 * - 0 on success, indicating the write lock was acquired.
415 * - EBUSY if the lock is currently held by another thread (read or write lock).
416 * - EINVAL if the `rwlock` is invalid.
417 *
418 * @note This function is non-blocking and returns immediately if the lock cannot
419 * be acquired. After successfully acquiring the write lock, the thread must
420 * release it using `pthread_rwlock_unlock`.
421 *
422 * @see pthread_rwlock_unlock
423 * @see pthread_rwlock_wrlock
424 * @see pthread_rwlock_timedwrlock
425 */
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)426 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
427 {
428 int result;
429
430 if (!rwlock)
431 return EINVAL;
432 if (rwlock->attr == -1)
433 pthread_rwlock_init(rwlock, NULL);
434
435 if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
436 return(result);
437
438 if (rwlock->rw_refcount != 0)
439 result = EBUSY; /* held by either writer or reader(s) */
440 else
441 rwlock->rw_refcount = -1; /* available, indicate a writer has it */
442
443 pthread_mutex_unlock(&rwlock->rw_mutex);
444
445 return(result);
446 }
447 RTM_EXPORT(pthread_rwlock_trywrlock);
448
449 /**
450 * @brief Release a read or write lock on a read-write lock.
451 *
452 * This function unlocks the specified read-write lock, releasing either a read
453 * lock or a write lock held by the calling thread. If the calling thread does
454 * not hold the lock, the behavior is undefined.
455 *
456 * @param rwlock A pointer to the read-write lock to be unlocked.
457 *
458 * @return
459 * - 0 on success.
460 * - EINVAL if the `rwlock` is invalid.
461 *
462 * @note
463 * - This function must only be called by the thread that successfully acquired
464 * the lock.
465 *
466 * @see pthread_rwlock_rdlock
467 * @see pthread_rwlock_wrlock
468 * @see pthread_rwlock_tryrdlock
469 * @see pthread_rwlock_trywrlock
470 */
pthread_rwlock_unlock(pthread_rwlock_t * rwlock)471 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
472 {
473 int result;
474
475 if (!rwlock)
476 return EINVAL;
477 if (rwlock->attr == -1)
478 pthread_rwlock_init(rwlock, NULL);
479
480 if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
481 return(result);
482
483 if (rwlock->rw_refcount > 0)
484 rwlock->rw_refcount--; /* releasing a reader */
485 else if (rwlock->rw_refcount == -1)
486 rwlock->rw_refcount = 0; /* releasing a writer */
487
488 /* give preference to waiting writers over waiting readers */
489 if (rwlock->rw_nwaitwriters > 0)
490 {
491 if (rwlock->rw_refcount == 0)
492 result = pthread_cond_signal(&rwlock->rw_condwriters);
493 }
494 else if (rwlock->rw_nwaitreaders > 0)
495 {
496 result = pthread_cond_broadcast(&rwlock->rw_condreaders);
497 }
498
499 pthread_mutex_unlock(&rwlock->rw_mutex);
500
501 return(result);
502 }
503 RTM_EXPORT(pthread_rwlock_unlock);
504
505 /**
506 * @brief Acquire a write lock on a read-write lock.
507 *
508 * This function locks the specified read-write lock for writing. If the lock
509 * is already held by another thread for reading or writing, the calling thread
510 * blocks until the lock becomes available.
511 *
512 * @param rwlock A pointer to the read-write lock to be locked.
513 *
514 * @return
515 * - 0 on success, indicating the write lock was acquired.
516 * - EINVAL if the `rwlock` is invalid.
517 * - EDEADLK if a deadlock condition is detected (optional; implementation-dependent).
518 *
519 * @note
520 * - A write lock is exclusive, meaning no other thread can acquire a read or
521 * write lock while a write lock is held.
522 * - The thread that successfully acquires the write lock must release it using
523 * `pthread_rwlock_unlock`.
524 *
525 * @see pthread_rwlock_unlock
526 * @see pthread_rwlock_trywrlock
527 * @see pthread_rwlock_timedwrlock
528 * @see pthread_rwlock_rdlock
529 */
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)530 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
531 {
532 int result;
533
534 if (!rwlock)
535 return EINVAL;
536 if (rwlock->attr == -1)
537 pthread_rwlock_init(rwlock, NULL);
538
539 if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0)
540 return(result);
541
542 while (rwlock->rw_refcount != 0)
543 {
544 rwlock->rw_nwaitwriters++;
545 /* rw_mutex will be released when waiting for rw_condwriters */
546 result = pthread_cond_wait(&rwlock->rw_condwriters, &rwlock->rw_mutex);
547 /* rw_mutex should have been taken again when returned from waiting */
548 rwlock->rw_nwaitwriters--;
549
550 if (result != 0)
551 break;
552 }
553
554 if (result == 0)
555 rwlock->rw_refcount = -1;
556
557 pthread_mutex_unlock(&rwlock->rw_mutex);
558
559 return(result);
560 }
561 RTM_EXPORT(pthread_rwlock_wrlock);
562
563