diff options
| author | bhgv <bhgv.empire@gmail.com> | 2020-05-10 02:59:23 +0300 |
|---|---|---|
| committer | bhgv <bhgv.empire@gmail.com> | 2020-05-10 02:59:23 +0300 |
| commit | 31b4edc67b75658ce5e2d41f2fc87331f4b26d49 (patch) | |
| tree | a7b6ea659fe62e0a7239f29170024f524595fb4d /libiot/pthread/_pthread.c | |
| parent | c76314f0f38f4ed028610a6db4452879a556b35f (diff) | |
a try to add support of FreeRTOS riscV-64 (k210 cpu). first step
Diffstat (limited to 'libiot/pthread/_pthread.c')
| -rw-r--r-- | libiot/pthread/_pthread.c | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/libiot/pthread/_pthread.c b/libiot/pthread/_pthread.c new file mode 100644 index 0000000..d0353e7 --- /dev/null +++ b/libiot/pthread/_pthread.c @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2015 - 2018, IBEROXARXA SERVICIOS INTEGRALES, S.L. + * Copyright (C) 2015 - 2018, Jaume Olivé Petrus (jolive@whitecatboard.org) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * * The WHITECAT logotype cannot be changed, you can remove it, but you + * cannot change it in any way. The WHITECAT logotype is: + * + * /\ /\ + * / \_____/ \ + * /_____________\ + * W H I T E C A T + * + * * Redistributions in binary form must retain all copyright notices printed + * to any local or remote output device. This include any reference to + * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may + * appear in the future. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Lua RTOS pthread implementation for FreeRTOS + * + */ + +#include "luartos.h" + +#include "esp_err.h" +#include "esp_attr.h" + +#include "thread.h" +#include "_pthread.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <sys/mutex.h> +#include <sys/queue.h> + +// A mutex to sync with critical parts +static struct mtx thread_mtx; + +// List of active threads. An active thread is one that is created +// and has not yet terminated. +static struct list active_threads; + +// List of inactive threads. An inactive thread is one that has yet +// terminated, but that was not joined. We must to store this threads +// in a list to allow joins after the thread termination. +static struct list inactive_threads; + +struct list key_list; + +static uint8_t inited = 0; + +// Arguments for the thread task +struct pthreadTaskArg { + struct pthread *thread; // Thread data + void *(*pthread_function)(void *); // Thread start routine + void *args; // Thread start routine arguments + xTaskHandle parent_task; // Handle of parent task +}; + +void pthreadTask(void *task_arguments); + +void _pthread_lock() { + mtx_lock(&thread_mtx); +} + +void _pthread_unlock() { + mtx_unlock(&thread_mtx); +} + +void _pthread_init() { + if (!inited) { + // Create mutexes + mtx_init(&thread_mtx, NULL, NULL, 0); + + // Init lists + lstinit(&active_threads, 1, LIST_NOT_INDEXED); + lstinit(&inactive_threads, 1, LIST_NOT_INDEXED); + + lstinit(&key_list, 1, LIST_DEFAULT); + + inited = 1; + } +} + +int _pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *args) { + BaseType_t res; + + // Get the creation attributes + pthread_attr_t cattr; + if (attr) { + // Creation attributes passed as function arguments, copy to thread data + memcpy(&cattr, attr, sizeof(pthread_attr_t)); + } else { + // Apply default creation attributes + pthread_attr_init(&cattr); + } + + // Create and populate the thread data + struct pthread *threadd; + + threadd = (struct pthread *) calloc(1, sizeof(struct pthread)); + if (!threadd) { + return EAGAIN; + } + + // Thread is active + threadd->active = 1; + + // Copy creation attributes + memcpy(&threadd->attr, &cattr, sizeof(pthread_attr_t)); + + // No task joined + threadd->joined_task = NULL; + + // No result + threadd->res = NULL; + + // Initialize signal handlers + pthread_t self; + + if ((self = pthread_self())) { + // Copy signal handlers from current thread + bcopy(((struct pthread *) self)->signals, threadd->signals, + sizeof(sig_t) * PTHREAD_NSIG); + } else { + // Set default signal handlers + int i; + + for (i = 0; i < PTHREAD_NSIG; i++) { + threadd->signals[i] = SIG_DFL; + } + } + + // Init clean list + lstinit(&threadd->clean_list, 1, LIST_DEFAULT); + + // Add thread to the thread list + res = lstadd(&active_threads, (void *) threadd, NULL); + if (res) { + free(threadd); + return EAGAIN; + } + + *thread = (pthread_t) threadd; + + // Create and populate the arguments for the thread task + struct pthreadTaskArg *taskArgs; + + taskArgs = (struct pthreadTaskArg *) calloc(1, + sizeof(struct pthreadTaskArg)); + if (!taskArgs) { + lstremove(&active_threads, (int) threadd, 1); + return EAGAIN; + } + + taskArgs->thread = threadd; + taskArgs->pthread_function = start_routine; + taskArgs->args = args; + taskArgs->parent_task = xTaskGetCurrentTaskHandle(); + + // This is the parent thread. Now we need to wait for the initialization of critical information + // that is provided by the pthreadTask: + // + // * Allocate Lua RTOS specific TCB parts, using local storage pointer assigned to pthreads + // * CPU core id when thread is running + // * The thread id, stored in Lua RTOS specific TCB parts + // * The Lua state, stored in Lua RTOS specific TCB parts + // + + // Create related task + int cpu = 0; + + if (cattr.schedparam.affinityset != CPU_INITIALIZER) { + if (CPU_ISSET(0, &cattr.schedparam.affinityset)) { + cpu = 0; + } else if (CPU_ISSET(1, &cattr.schedparam.affinityset)) { + cpu = 1; + } + } else { + cpu = tskNO_AFFINITY; + } + + xTaskHandle xCreatedTask; // Related task + + if (cpu == tskNO_AFFINITY) { + res = xTaskCreate(pthreadTask, "thread", cattr.stacksize, taskArgs, + cattr.schedparam.sched_priority, &xCreatedTask); + } else { + res = xTaskCreatePinnedToCore(pthreadTask, "thread", cattr.stacksize, + taskArgs, cattr.schedparam.sched_priority, &xCreatedTask, cpu); + } + + if (res != pdPASS) { + // Remove from thread list + lstremove(&active_threads, *thread, 1); + free(taskArgs); + + return EAGAIN; + } + + // Wait for the initialization of Lua RTOS specific TCB parts + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + + threadd->task = xCreatedTask; + + return 0; +} + +int _pthread_join(pthread_t id, void **value_ptr) { + _pthread_lock(); + + // Get thread + struct pthread *thread; + + if (lstget(&active_threads, id, (void **) &thread) != 0) { + // Thread not found in active threads, may be is inactive + if (lstget(&inactive_threads, id, (void **) &thread) != 0) { + // Thread not found + _pthread_unlock(); + return ESRCH; + } + } + + // Check that thread is joinable + if (thread->attr.detachstate == PTHREAD_CREATE_DETACHED) { + _pthread_unlock(); + + return EINVAL; + } + + if (thread->active) { + if (thread->joined_task != NULL) { + _pthread_unlock(); + + // Another thread is already waiting to join with this thread + return EINVAL; + } + + // Join the current task to the thread + thread->joined_task = xTaskGetCurrentTaskHandle(); + } + + if (thread->active) { + _pthread_unlock(); + + // Wait until the thread ends + uint32_t ret; + + xTaskNotifyWait(0, 0, &ret, portMAX_DELAY); + + if (value_ptr) { + *value_ptr = (void *) ret; + } + } else { + if (value_ptr) { + *value_ptr = (void *) thread->res; + } + + _pthread_free((pthread_t) thread, 1); + + _pthread_unlock(); + } + + return 0; +} + +int _pthread_detach(pthread_t id) { + _pthread_lock(); + + // Get thread + struct pthread *thread; + + if (lstget(&active_threads, id, (void **) &thread) != 0) { + // Thread not found in active threads, may be is inactive + if (lstget(&inactive_threads, id, (void **) &thread) != 0) { + // Thread not found + _pthread_unlock(); + return ESRCH; + } + } + + if (thread->attr.detachstate == PTHREAD_CREATE_DETACHED) { + return EINVAL; + } + + thread->attr.detachstate = PTHREAD_CREATE_DETACHED; + + lstremove(&inactive_threads, id, 1); + + _pthread_unlock(); + + return 0; +} + +void _pthread_cleanup_push(void (*routine)(void *), void *arg) { + struct pthread *thread; + struct pthread_clean *clean; + + _pthread_lock(); + + // Get current thread + if (lstget(&active_threads, pthread_self(), (void **) &thread) == 0) { + // Create the clean structure + clean = (struct pthread_clean *) malloc(sizeof(struct pthread_clean)); + if (!clean) { + _pthread_unlock(); + return; + } + + clean->clean = routine; + clean->args = arg; + + // Add to clean list + lstadd(&thread->clean_list, clean, NULL); + } + + _pthread_unlock(); +} + +void _pthread_cleanup_pop(int execute) { + struct pthread *thread; + struct pthread_clean *clean; + + _pthread_lock(); + + // Get current thread + if (lstget(&active_threads, pthread_self(), (void **) &thread) == 0) { + // Get last element in clean list, so we must pop handlers in reverse order + int index; + + if ((index = lstlast(&thread->clean_list)) >= 0) { + if (lstget(&thread->clean_list, index, (void **) &clean) == 0) { + // Execute handler + if (clean->clean && execute) { + clean->clean(clean->args); + } + + // Remove handler from list + lstremove(&thread->clean_list, index, 1); + } + } + } + + _pthread_unlock(); +} + +void _pthread_cleanup() { + struct pthread *thread; + struct pthread_clean *clean; + + _pthread_lock(); + + // Get current thread + if (lstget(&active_threads, pthread_self(), (void **) &thread) == 0) { + // Get all elements in clean list, in reverse order + int index; + + while ((index = lstlast(&thread->clean_list)) >= 0) { + if (lstget(&thread->clean_list, index, (void **) &clean) == 0) { + // Execute handler + if (clean->clean) { + clean->clean(clean->args); + } + + // Remove handler from list + lstremove(&thread->clean_list, index, 1); + } + } + } + + _pthread_unlock(); +} + +int _pthread_free(pthread_t id, int destroy) { + // Get thread + struct pthread *thread = (struct pthread *) id; + + // Destroy clean list + lstdestroy(&thread->clean_list, 1); + + // Remove thread + lstremove(&active_threads, id, destroy); + lstremove(&inactive_threads, id, destroy); + + return 0; +} + +sig_t _pthread_signal(int s, sig_t h) { + struct pthread *thread; // Current thread + sig_t prev_h; // Previous handler + + if (s > PTHREAD_NSIG) { + return NULL; + } + + if (lstget(&active_threads, pthread_self(), (void **) &thread) == 0) { + // Add handler + prev_h = thread->signals[s]; + thread->signals[s] = h; + + return prev_h; + } + + return NULL; +} + +void _pthread_exec_signal(int dst, int s) { + if (s > PTHREAD_NSIG) { + return; + } + + // Get destination thread + struct pthread *thread; + + if (lstget(&active_threads, dst, (void **) &thread) == 0) { + // If destination thread has a handler for the signal, execute it + if ((thread->signals[s] != SIG_DFL) + && (thread->signals[s] != SIG_IGN)) { + if (thread->is_delayed && ((s == (SIGINT) || (s == SIGABRT)))) { + xTaskAbortDelay(thread->task); + } + + thread->signals[s](s); + } + } +} + +int IRAM_ATTR _pthread_has_signal(int dst, int s) { + if (s > PTHREAD_NSIG) { + return 0; + } + + // Get destination thread + struct pthread *thread; + + if (lstget(&active_threads, dst, (void **) &thread) == 0) { + return ((thread->signals[s] != SIG_DFL) + && (thread->signals[s] != SIG_IGN)); + } + + return 0; +} + +int _pthread_sleep(uint32_t msecs) { + struct pthread *thread; + int res; + + // Get the current thread + res = lstget(&active_threads, pthread_self(), (void **) &thread); + if (res) { + // Its not a thread, simply delay task + vTaskDelay(msecs / portTICK_PERIOD_MS); + return 0; + } + + // Is a thread. Mark it as delayed. + thread->is_delayed = 1; + thread->delay_interrupted = 0; + + vTaskDelay(msecs / portTICK_PERIOD_MS); + + thread->is_delayed = 0; + + if (thread->delay_interrupted) { + thread->delay_interrupted = 0; + return -1; + } else { + return 0; + } +} + +int _pthread_stop(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + // Stop + vTaskDelete(thread->task); + + return 0; +} + +int _pthread_core(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + return (int) ucGetCoreID(thread->task); +} + +int _pthread_stack(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + return (int) uxGetStack(thread->task); +} + +int _pthread_stack_free(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + return (int) uxTaskGetStackHighWaterMark(thread->task); +} + +int _pthread_suspend(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + UBaseType_t priority = uxTaskPriorityGet(NULL); + vTaskPrioritySet(NULL, 22); + + // Suspend + uxSetThreadStatus(thread->task, StatusSuspended); + vTaskSuspend(thread->task); + + vTaskPrioritySet(NULL, priority); + + return 0; +} + +int _pthread_resume(pthread_t id) { + struct pthread *thread; + int res; + + // Get thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return res; + } + + // Resume + uxSetThreadStatus(thread->task, StatusRunning); + vTaskResume(thread->task); + + return 0; +} + +struct pthread *_pthread_get(pthread_t id) { + struct pthread *thread; + int res; + + // Get the thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return NULL; + } + + return thread; +} + +// This is the callback function for free Lua RTOS specific TCB parts +static void pthreadLocaleStoragePointerCallback(int index, void* data) { + if (index == THREAD_LOCAL_STORAGE_POINTER_ID) { + free(data); + } +} + +void pthreadTask(void *taskArgs) { + struct pthreadTaskArg *args; // Task arguments + struct pthread *thread; // Current thread + lua_rtos_tcb_t *lua_rtos_tcb; // Lua RTOS specific TCB parts + + // This is the new thread + args = (struct pthreadTaskArg *) taskArgs; + + // Get thread + thread = (struct pthread *) args->thread; + + // Allocate and initialize Lua RTOS specific TCB parts, and store it into a FreeRTOS + // local storage pointer + lua_rtos_tcb = (lua_rtos_tcb_t *) calloc(1, sizeof(lua_rtos_tcb_t)); + assert(lua_rtos_tcb != NULL); + + lua_rtos_tcb->status = StatusRunning; + + vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, + THREAD_LOCAL_STORAGE_POINTER_ID, (void *) lua_rtos_tcb, + pthreadLocaleStoragePointerCallback); + + // Set thread id + uxSetThreadId((UBaseType_t) args->thread); + + // Call additional thread init function + if (args->thread->attr.init_func) { + args->thread->attr.init_func(args->args); + } + + // Lua RTOS specific TCB parts are set, parent thread can continue + xTaskNotify(args->parent_task, 0, eNoAction); + + if (args->thread->attr.schedparam.initial_state + == PTHREAD_INITIAL_STATE_SUSPEND) { + vTaskSuspend(NULL); + } + + // Call start function + void *ret = args->pthread_function(args->args); + + _pthread_lock(); + + if (args->thread->attr.detachstate == PTHREAD_CREATE_JOINABLE) { + if (thread->joined_task != NULL) { + // Notify to joined thread + xTaskNotify(thread->joined_task, (uint32_t) ret, + eSetValueWithOverwrite); + + thread->joined_task = NULL; + + _pthread_free((pthread_t) thread, 1); + } else { + _pthread_free((pthread_t) thread, 0); + + // Put the thread into the inactive threads, because join can occur later, and + // the thread's result must be stored + thread->active = 0; + thread->res = ret; + + lstadd(&inactive_threads, (void *) thread, NULL); + } + } else { + _pthread_free((pthread_t) thread, 1); + } + + // Free args + free(taskArgs); + + _pthread_unlock(); + + // End related task + vTaskDelete(NULL); +} + +int pthread_cancel(pthread_t thread) { + return 0; +} + +esp_err_t esp_pthread_init(void) { + _pthread_init(); + + return ESP_OK; +} + +int pthread_setname_np(pthread_t id, const char *name) { + struct pthread *thread; + int res; + + // Sanity checks + if (strlen(name) > configMAX_TASK_NAME_LEN - 1) { + return ERANGE; + } + + // Get the thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return EINVAL; + } + + // Get the TCB task for this thread + tskTCB_t *task = (tskTCB_t *) (thread->task); + + // Copy the name into the TCB + strncpy(task->pcTaskName, name, configMAX_TASK_NAME_LEN - 1); + + return 0; +} + +int pthread_getname_np(pthread_t id, char *name, size_t len) { + struct pthread *thread; + int res; + + // Get the thread + res = lstget(&active_threads, id, (void **) &thread); + if (res) { + return EINVAL; + } + + // Get the TCB task for this thread + tskTCB_t *task = (tskTCB_t *) (thread->task); + + // Sanity checks + if (strlen(task->pcTaskName) < len - 1) { + return ERANGE; + } + + // Copy the name from the TCB + strncpy(name, task->pcTaskName, configMAX_TASK_NAME_LEN - 1); + + return 0; +} |
