mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 09:01:40 +01:00 
			
		
		
		
	
		
			
	
	
		
			261 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			261 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int	pthread_rwlock_init (pthread_rwlock_t *rwlock,
							 | 
						||
| 
								 | 
							
											     const pthread_rwlockattr_t *attr)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int	pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int	pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int	pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int	pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    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)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								}
							 |