mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 00:51:42 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			318 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-FileCopyrightText: 2021-2023 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_tryrdlock (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_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;
 | 
						|
}
 | 
						|
 | 
						|
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_trywrlock (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_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;
 | 
						|
}
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
}
 |