| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  |  * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: Apache-2.0 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <pthread.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdatomic.h>
 | 
					
						
							|  |  |  | #include "esp_err.h"
 | 
					
						
							|  |  |  | #include "esp_attr.h"
 | 
					
						
							|  |  |  | #include "sys/queue.h"
 | 
					
						
							|  |  |  | #include "freertos/FreeRTOS.h"
 | 
					
						
							|  |  |  | #include "freertos/task.h"
 | 
					
						
							|  |  |  | #include "freertos/semphr.h"
 | 
					
						
							|  |  |  | #include "soc/soc_memory_layout.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "pthread_internal.h"
 | 
					
						
							|  |  |  | #include "esp_pthread.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "esp_log.h"
 | 
					
						
							|  |  |  | const static char *TAG = "pthread_rw_lock"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** pthread rw_mutex FreeRTOS wrapper */ | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     pthread_cond_t cv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_t resource_mutex; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * Number of current readers holding this lock, negative number means waiting readers | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     int8_t active_readers; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint8_t active_writers; | 
					
						
							|  |  |  |     uint8_t waiting_writers; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } esp_pthread_rwlock_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define WRITER_QUEUE_SIZE 4
 | 
					
						
							|  |  |  | #define READER_QUEUE_SIZE 4
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_init(pthread_rwlock_t *rwlock, | 
					
						
							|  |  |  |                         const pthread_rwlockattr_t *attr) | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     int result; | 
					
						
							|  |  |  |     if (!rwlock) { | 
					
						
							|  |  |  |         return EINVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (attr) { | 
					
						
							|  |  |  |         // TODO: implement attributes in IDF-4284
 | 
					
						
							|  |  |  |         return ENOSYS; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock = (esp_pthread_rwlock_t*) calloc(1, sizeof(esp_pthread_rwlock_t)); | 
					
						
							|  |  |  |     if (esp_rwlock == NULL) { | 
					
						
							|  |  |  |         return ENOMEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     result = pthread_mutex_init(&esp_rwlock->resource_mutex, NULL); | 
					
						
							|  |  |  |     if (result != 0) { | 
					
						
							|  |  |  |         free(esp_rwlock); | 
					
						
							|  |  |  |         return ENOMEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     result = pthread_cond_init(&esp_rwlock->cv, NULL); | 
					
						
							|  |  |  |     if (result != 0) { | 
					
						
							|  |  |  |         pthread_mutex_destroy(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |         free(esp_rwlock); | 
					
						
							|  |  |  |         return ENOMEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock->active_readers = 0; | 
					
						
							|  |  |  |     esp_rwlock->active_writers = 0; | 
					
						
							|  |  |  |     esp_rwlock->waiting_writers = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *rwlock = (pthread_rwlock_t) esp_rwlock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_rwlock_init_if_static(pthread_rwlock_t *rwlock) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int res = 0; | 
					
						
							|  |  |  |     if ((intptr_t) *rwlock == PTHREAD_RWLOCK_INITIALIZER) { | 
					
						
							|  |  |  |         portENTER_CRITICAL(&pthread_lazy_init_lock); | 
					
						
							|  |  |  |         if ((intptr_t) *rwlock == PTHREAD_RWLOCK_INITIALIZER) { | 
					
						
							|  |  |  |             res = pthread_rwlock_init(rwlock, NULL); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         portEXIT_CRITICAL(&pthread_lazy_init_lock); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ESP_LOGV(TAG, "%s %p", __FUNCTION__, rwlock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!rwlock) { | 
					
						
							|  |  |  |         return EINVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((intptr_t) *rwlock == PTHREAD_RWLOCK_INITIALIZER) { | 
					
						
							|  |  |  |         return 0; // Static rwlock was never initialized
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     if (esp_rwlock == NULL) { | 
					
						
							|  |  |  |         return EINVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO: necessary?
 | 
					
						
							|  |  |  |     pthread_mutex_lock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (esp_rwlock->active_readers != 0 || esp_rwlock->active_writers > 0 || esp_rwlock->waiting_writers > 0) { | 
					
						
							|  |  |  |         pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |         return EBUSY; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // delete whole lock
 | 
					
						
							|  |  |  |     pthread_cond_destroy(&esp_rwlock->cv); | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     pthread_mutex_destroy(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(esp_rwlock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int checkrw_lock(pthread_rwlock_t *rwlock) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (rwlock == NULL) { | 
					
						
							|  |  |  |         return EINVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = pthread_rwlock_init_if_static(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     if (esp_rwlock == NULL) { | 
					
						
							|  |  |  |         return EINVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = checkrw_lock(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     res = pthread_mutex_lock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (esp_rwlock->active_writers == 0) { | 
					
						
							|  |  |  |         esp_rwlock->active_readers++; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         while (true) { | 
					
						
							|  |  |  |             pthread_cond_wait(&esp_rwlock->cv, &esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |             if (esp_rwlock->active_writers == 0 && esp_rwlock->waiting_writers == 0) { | 
					
						
							|  |  |  |                 esp_rwlock->active_readers++; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) | 
					
						
							| 
									
										
										
										
											2023-04-17 14:45:26 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = checkrw_lock(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     res = pthread_mutex_trylock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (esp_rwlock->active_writers == 0) { | 
					
						
							|  |  |  |         esp_rwlock->active_readers++; | 
					
						
							|  |  |  |         res = 0; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         res = EBUSY; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = checkrw_lock(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     res = pthread_mutex_lock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock->waiting_writers++; | 
					
						
							|  |  |  |     while (esp_rwlock->active_readers > 0 || esp_rwlock->active_writers > 0) { | 
					
						
							|  |  |  |         pthread_cond_wait(&esp_rwlock->cv, &esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     esp_rwlock->waiting_writers--; | 
					
						
							|  |  |  |     esp_rwlock->active_writers++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-04-17 14:45:26 +08:00
										 |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = checkrw_lock(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     res = pthread_mutex_trylock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (esp_rwlock->active_readers > 0 || | 
					
						
							|  |  |  |             esp_rwlock->active_writers > 0 || | 
					
						
							|  |  |  |             esp_rwlock->waiting_writers > 0) { // the last check for waiting_writers is to avoid skipping the queue
 | 
					
						
							|  |  |  |         res = EBUSY; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         esp_rwlock->active_writers++; | 
					
						
							|  |  |  |         res = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 10:00:06 +08:00
										 |  |  | int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) | 
					
						
							| 
									
										
										
										
											2021-08-27 13:00:10 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     esp_pthread_rwlock_t *esp_rwlock; | 
					
						
							|  |  |  |     int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = checkrw_lock(rwlock); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     esp_rwlock = (esp_pthread_rwlock_t *)*rwlock; | 
					
						
							|  |  |  |     res = pthread_mutex_lock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(!(esp_rwlock->active_readers > 0 && esp_rwlock->active_writers > 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (esp_rwlock->active_readers > 0) { | 
					
						
							|  |  |  |         // we are a reader
 | 
					
						
							|  |  |  |         esp_rwlock->active_readers--; | 
					
						
							|  |  |  |         if (esp_rwlock->active_readers == 0) { | 
					
						
							|  |  |  |             pthread_cond_broadcast(&esp_rwlock->cv); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // we are a writer
 | 
					
						
							|  |  |  |         esp_rwlock->active_writers = 0; | 
					
						
							|  |  |  |         pthread_cond_broadcast(&esp_rwlock->cv); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&esp_rwlock->resource_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Hook function to force linking this file */ | 
					
						
							|  |  |  | void pthread_include_pthread_rwlock_impl(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } |