diff options
Diffstat (limited to 'libiot/pthread')
| -rw-r--r-- | libiot/pthread/_pthread.c | 743 | ||||
| -rw-r--r-- | libiot/pthread/_pthread.h | 241 | ||||
| -rw-r--r-- | libiot/pthread/attr.c | 244 | ||||
| -rw-r--r-- | libiot/pthread/cond.c | 204 | ||||
| -rw-r--r-- | libiot/pthread/create.c | 55 | ||||
| -rw-r--r-- | libiot/pthread/join.c | 70 | ||||
| -rw-r--r-- | libiot/pthread/key.c | 163 | ||||
| -rw-r--r-- | libiot/pthread/kill.c | 60 | ||||
| -rw-r--r-- | libiot/pthread/mkfile | 10 | ||||
| -rw-r--r-- | libiot/pthread/mutex.c | 241 | ||||
| -rw-r--r-- | libiot/pthread/once.c | 67 | ||||
| -rw-r--r-- | libiot/pthread/self.c | 53 | ||||
| -rw-r--r-- | libiot/pthread/test/pthread-cleanup.c | 200 | ||||
| -rw-r--r-- | libiot/pthread/test/pthread-cond.c | 107 | ||||
| -rw-r--r-- | libiot/pthread/test/pthread-join.c | 85 | ||||
| -rw-r--r-- | libiot/pthread/test/pthread-once.c | 68 |
16 files changed, 2611 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; +} diff --git a/libiot/pthread/_pthread.h b/libiot/pthread/_pthread.h new file mode 100644 index 0000000..c6df9aa --- /dev/null +++ b/libiot/pthread/_pthread.h @@ -0,0 +1,241 @@ +/* + * 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" + +#ifndef __PTHREAD_H +#define __PTHREAD_H + +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/adds.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +#include <pthread.h> + +#include <errno.h> + +#include <sys/mutex.h> +#include <sys/list.h> +#include <sys/time.h> + +#include <signal.h> + + +#define CPU_INITIALIZER 0 + +// Add CPU cpu to set +#define CPU_SET(ncpu, cpuset) \ + *(cpuset) |= (1 << ncpu) + +// Test to see if CPU cpu is a member of set. +#define CPU_ISSET(ncpu, cpuset) \ + (*(cpuset) & (1 << ncpu)) + +// Each thread maintains a signal handler copy. Typically there are around 32 defined +// signals, but not signals are required for applications. For example, in Lua only +// SIGINT is used. +// +// This defines how many signals will be available in threads +#define PTHREAD_NSIG (SIGINT + 1) + +// 1 for activate debug log when thread can't lock a mutex +#define PTHREAD_MTX_DEBUG 0 + +#if PTHREAD_MTX_DEBUG +#define PTHREAD_MTX_LOCK_TIMEOUT (3000 / portTICK_PERIOD_MS) +#define PTHREAD_MTX_DEBUG_LOCK() printf("phread can't lock\n"); +#else +#define PTHREAD_MTX_LOCK_TIMEOUT portMAX_DELAY +#define PTHREAD_MTX_DEBUG_LOCK() +#endif + +// Minimal stack size per thread +#define PTHREAD_STACK_MIN (1024 * 2) + +#define PTHREAD_CREATE_DETACHED 0 + +// Initial states for a thread +#define PTHREAD_INITIAL_STATE_RUN 1 +#define PTHREAD_INITIAL_STATE_SUSPEND 2 + +// Thread types +#define PTHREAD_TYPE_DEFAULT 0 +#define PTHREAD_TYPE_LUA 1 + +// Required structures and types +struct pthread_mutex { + SemaphoreHandle_t sem; + int owner; + int type; +}; + +struct pthread_cond { + struct mtx mutex; + EventGroupHandle_t ev; + int referenced; +}; + +struct pthread_key_specific { + pthread_t thread; + const void *value; +}; + +struct pthread_key { + struct list specific; + void (*destructor)(void*); +}; + +struct pthread_join { + QueueHandle_t queue; +}; + +struct pthread_clean { + void (*clean)(void*); + void *args; +}; + +struct pthread { + struct list clean_list; + sig_t signals[PTHREAD_NSIG]; + xTaskHandle task; + uint8_t is_delayed; + uint8_t delay_interrupted; + uint8_t active; + xTaskHandle joined_task; + void *res; + pthread_attr_t attr; +}; + +// Helper functions, only for internal use +void _pthread_lock(); +void _pthread_unlock(); +void _pthread_init(); +int _pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *args); +int _pthread_join(pthread_t id, void **value_ptr); +int _pthread_free(pthread_t id, int destroy); +sig_t _pthread_signal(int s, sig_t h); +void _pthread_exec_signal(int dst, int s); +void _pthread_process_signal(); +int _pthread_has_signal(int dst, int s); +int _pthread_stop(pthread_t id); +int _pthread_suspend(pthread_t id); +int _pthread_resume(pthread_t id); +int _pthread_core(pthread_t id); +sig_t _pthread_signal(int s, sig_t h); +int _pthread_get_prio(); +int _pthread_stack_free(pthread_t id); +int _pthread_stack(pthread_t id); +struct pthread *_pthread_get(pthread_t id); +int _pthread_sleep(uint32_t msecs); +void _pthread_cleanup_push(void (*routine)(void *), void *arg); +void _pthread_cleanup_pop(int execute); +void _pthread_cleanup(); +int _pthread_detach(pthread_t id); + +// API functions +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); + +int pthread_mutex_init(pthread_mutex_t *mut, const pthread_mutexattr_t *attr); +int pthread_mutex_lock(pthread_mutex_t *mut); +int pthread_mutex_unlock(pthread_mutex_t *mut); +int pthread_mutex_trylock(pthread_mutex_t *mut); +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); +int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +int pthread_cond_destroy(pthread_cond_t *cond); +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); +int pthread_cond_signal(pthread_cond_t *cond); + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); +int pthread_setcancelstate(int state, int *oldstate); +int pthread_key_create(pthread_key_t *k, void (*destructor)(void*)); +int pthread_setspecific(pthread_key_t k, const void *value); +void *pthread_getspecific(pthread_key_t k); +int pthread_join(pthread_t thread, void **value_ptr); +int pthread_cancel(pthread_t thread); +int pthread_kill(pthread_t thread, int signal); + +int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset); +int pthread_attr_getaffinity_np(const pthread_attr_t *attr, size_t cpusetsize, cpu_set_t *cpuset); +int pthread_setname_np(pthread_t id, const char *name); +int pthread_getname_np(pthread_t id, char *name, size_t len); +int pthread_attr_setinitialstate_np(pthread_attr_t *attr, int initial_state); +int pthread_attr_setinitfunc_np(pthread_attr_t *attr, void (*init_func)(void *)); + +pthread_t pthread_self(void); + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *args); + +#define pthread_exit(ret) \ +do { \ + _pthread_cleanup(); \ + return ((void *)ret); \ +} while (0) + + +#define pthread_cleanup_push(routine, args) \ +do { \ + _pthread_cleanup_push(routine, args) + +#define pthread_cleanup_pop(exec) \ + _pthread_cleanup_pop(exec); \ +} while(0) + +#endif /* __PTHREAD_H */ + diff --git a/libiot/pthread/attr.c b/libiot/pthread/attr.c new file mode 100644 index 0000000..fe5c2a6 --- /dev/null +++ b/libiot/pthread/attr.c @@ -0,0 +1,244 @@ +/* + * 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 "_pthread.h" + +int pthread_attr_init(pthread_attr_t *attr) { + if (!attr) { + return EINVAL; + } + + attr->is_initialized = 1; + + attr->stacksize = (CONFIG_LUA_RTOS_LUA_THREAD_STACK_SIZE >= PTHREAD_STACK_MIN?CONFIG_LUA_RTOS_LUA_THREAD_STACK_SIZE:PTHREAD_STACK_MIN); + + attr->schedparam.initial_state = PTHREAD_INITIAL_STATE_RUN; + attr->schedparam.sched_priority = CONFIG_LUA_RTOS_LUA_TASK_PRIORITY; + attr->schedparam.affinityset = CPU_INITIALIZER; // No affinity + attr->init_func = NULL; + attr->detachstate = PTHREAD_CREATE_JOINABLE; + + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *attr) { + if (!attr) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { + if (!attr) { + return EINVAL; + } + + if (stacksize < PTHREAD_STACK_MIN) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + attr->stacksize = stacksize; + + return 0; +} + +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) { + if (!attr || !stacksize) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + *stacksize = attr->stacksize; + + return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) { + if (!attr || !param) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + if ((param->sched_priority > configMAX_PRIORITIES - 1) || (param->sched_priority < 1)) { + return EINVAL; + } + + attr->schedparam.sched_priority = param->sched_priority; + + return 0; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) { + if (!attr || !param) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + param->sched_priority = attr->schedparam.sched_priority; + + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { + if (!attr) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + if ((detachstate != PTHREAD_CREATE_DETACHED) && (detachstate != PTHREAD_CREATE_JOINABLE)) { + return EINVAL; + } + + attr->detachstate = detachstate; + + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) { + if (!attr || !detachstate) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + *detachstate = attr->detachstate; + + return 0; +} + +int pthread_setcancelstate(int state, int *oldstate) { + return 0; +} + +int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset) { + if (!attr || !cpuset) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + if ((*cpuset < 0) || (*cpuset > portNUM_PROCESSORS)) { + return EINVAL; + } + + attr->schedparam.affinityset = *cpuset; + + return 0; +} + +int pthread_attr_getaffinity_np(const pthread_attr_t *attr, size_t cpusetsize, cpu_set_t *cpuset) { + if (!attr || !cpuset) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + if ((*cpuset < 0) || (*cpuset > portNUM_PROCESSORS)) { + return EINVAL; + } + + *cpuset = attr->schedparam.affinityset; + + return 0; +} + +int pthread_attr_setinitialstate_np(pthread_attr_t *attr, int initial_state) { + if (!attr) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + if ((initial_state != PTHREAD_INITIAL_STATE_RUN) && (initial_state != PTHREAD_INITIAL_STATE_SUSPEND)) { + return EINVAL; + } + + attr->schedparam.initial_state = initial_state; + + return 0; +} + +int pthread_attr_setinitfunc_np(pthread_attr_t *attr, void (*init_routine)(void *)) { + if (!attr || !init_routine) { + return EINVAL; + } + + if (!attr->is_initialized) { + return EINVAL; + } + + attr->init_func = init_routine; + + return 0; +} diff --git a/libiot/pthread/cond.c b/libiot/pthread/cond.c new file mode 100644 index 0000000..288e548 --- /dev/null +++ b/libiot/pthread/cond.c @@ -0,0 +1,204 @@ +/* + * 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 "_pthread.h" + +#include <sys/mutex.h> +#include <sys/time.h> + +#if configUSE_16_BIT_TICKS +#define BITS_PER_EVENT_GROUP 8 +#else +#define BITS_PER_EVENT_GROUP 24 +#endif + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { + // Avoid to reinitialize the object referenced by cond, a previously + // initialized, but not yet destroyed, condition variable + if (*cond != PTHREAD_COND_INITIALIZER) { + return EBUSY; + } + + // Initialize the internal cond structure + struct pthread_cond *scond = calloc(1, sizeof(struct pthread_cond)); + if (!scond) { + return ENOMEM; + } + + // Init the cond mutex + mtx_init(&scond->mutex, NULL, NULL, 0); + if (!scond->mutex.lock) { + free(scond); + return ENOMEM; + } + + // Create the event group + scond->ev = xEventGroupCreate(); + if (scond->ev == NULL) { + mtx_destroy(&scond->mutex); + free(scond); + return ENOMEM; + } + + // Return the cond reference + *cond = (pthread_cond_t)scond; + + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) { + if (*cond == PTHREAD_COND_INITIALIZER) { + return EINVAL; + } + + struct pthread_cond *scond = (struct pthread_cond *) *cond; + + mtx_lock(&scond->mutex); + + if (scond->referenced > 0) { + mtx_unlock(&scond->mutex); + + return EBUSY; + } + + mtx_destroy(&scond->mutex); + + return 0; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { + return pthread_cond_timedwait(cond, mutex, NULL); +} + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) { + + if (*cond == PTHREAD_COND_INITIALIZER) { + return EINVAL; + } + + struct pthread_cond *scond = (struct pthread_cond *)*cond; + + // Find an unused bit in the event group to sync the condition + mtx_lock(&scond->mutex); + + uint8_t bit; + for(bit=0;bit < BITS_PER_EVENT_GROUP;bit++) { + if (!((1 << bit) & scond->referenced)) { + scond->referenced |= (1 << bit); + + break; + } + } + + mtx_unlock(&scond->mutex); + + if (bit >= BITS_PER_EVENT_GROUP) { + // All bits are used in the event group. This is a rare condition, because + // in Lua RTOS an event group has 24 bits, which means that 24 conditions + // (or 24 threads) can be handled for each condition variable. + // + // Although is not part of the pthread specification we indicate this + // condition returning the EINVAL error code. + return EINVAL; + } + + // Get the timeout in ticks + TickType_t ticks; + + if (!abstime) { + ticks = portMAX_DELAY; + } else { + struct timeval now, future, diff; + + gettimeofday(&now, NULL); + + future.tv_sec = abstime->tv_sec; + future.tv_usec = abstime->tv_nsec / 1000; + + if (timercmp(&future, &now, <)) { + return ETIMEDOUT; + } else { + timersub(&future, &now, &diff); + ticks = ((diff.tv_sec * 1000) + (diff.tv_usec / 1000)) / portTICK_PERIOD_MS; + } + } + + pthread_mutex_unlock(mutex); + EventBits_t uxBits = xEventGroupWaitBits(scond->ev, (1 << bit), pdTRUE, pdTRUE, ticks); + if (!(uxBits & (1 << bit))) { + pthread_mutex_lock(mutex); + scond->referenced &= ~(1 << bit); + pthread_mutex_unlock(mutex); + + return ETIMEDOUT; + } + pthread_mutex_lock(mutex); + + return 0; +} + +int pthread_cond_signal(pthread_cond_t *cond) { + if (*cond == PTHREAD_COND_INITIALIZER) { + return EINVAL; + } + + struct pthread_cond *scond = (struct pthread_cond *)*cond; + + mtx_lock(&scond->mutex); + + uint8_t bit; + for(bit=0;bit < BITS_PER_EVENT_GROUP;bit++) { + if ((1 << bit) & scond->referenced) { + xEventGroupSetBits(scond->ev, (1 << bit)); + + scond->referenced &= ~(1 << bit); + } + } + + mtx_unlock(&scond->mutex); + + return 0; +} diff --git a/libiot/pthread/create.c b/libiot/pthread/create.c new file mode 100644 index 0000000..fe591c7 --- /dev/null +++ b/libiot/pthread/create.c @@ -0,0 +1,55 @@ +/* + * 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 "_pthread.h" + +#include <pthread.h> + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *args) { + + return _pthread_create(thread, attr, start_routine, args); +} diff --git a/libiot/pthread/join.c b/libiot/pthread/join.c new file mode 100644 index 0000000..bd173f3 --- /dev/null +++ b/libiot/pthread/join.c @@ -0,0 +1,70 @@ +/* + * 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 "_pthread.h" + +int pthread_join(pthread_t thread, void **value_ptr) { + return _pthread_join(thread, value_ptr); +} + +int pthread_detach(pthread_t thread) { + return _pthread_detach(thread); +} + +int sched_yield( void ) { + return 0; +} + +int pthread_equal(pthread_t t1, pthread_t t2) { + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { + return ENOSYS; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { + return 0; +} diff --git a/libiot/pthread/key.c b/libiot/pthread/key.c new file mode 100644 index 0000000..ebb757f --- /dev/null +++ b/libiot/pthread/key.c @@ -0,0 +1,163 @@ +/* + * 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 "_pthread.h" + +#include <stdlib.h> + +extern struct list key_list; + +int pthread_key_create(pthread_key_t *k, void (*destructor)(void*)) { + struct pthread_key *key; + int res; + + // Allocate space for the key + key = (struct pthread_key *)malloc(sizeof(struct pthread_key)); + if (!key) { + return ENOMEM; + } + + // Init key + key->destructor = destructor; + + lstinit(&key->specific, 1, LIST_DEFAULT); + + // Add key to key list + res = lstadd(&key_list, (void *)key, (int*)k); + if (res) { + free(key); + return res; + } + + return 0; +} + +int pthread_setspecific(pthread_key_t k, const void *value) { + struct pthread_key_specific *specific; + struct pthread_key *key; + pthread_t thread; + int res; + int index; + + // Get key + res = lstget(&key_list, k, (void **)&key); + if (res) { + return res; + } + + if (value) { + // Allocate space for specific + specific = (struct pthread_key_specific *)malloc(sizeof(struct pthread_key_specific)); + if (!specific) { + return ENOMEM; + } + + specific->thread = pthread_self(); + specific->value = value; + + lstadd(&key->specific, (void **)specific, &index); + } else { + thread = pthread_self(); + + index = lstfirst(&key->specific); + while (index >= 0) { + lstget(&key->specific, index, (void **)&specific); + + if (specific->thread == thread) { + lstremove(&key->specific, k, 1); + break; + } + + index = lstnext(&key->specific, index); + } + } + + return 0; +} + +void *pthread_getspecific(pthread_key_t k) { + struct pthread_key_specific *specific; + struct pthread_key *key; + pthread_t thread; + int res; + int index; + + // Get key + res = lstget(&key_list, k, (void **)&key); + if (res) { + return NULL; + } + + // Get specific value + thread = pthread_self(); + + index = lstfirst(&key->specific); + while (index >= 0) { + lstget(&key->specific, index, (void **)&specific); + + if (specific->thread == thread) { + return (void *)specific->value; + } + + index = lstnext(&key->specific, index); + } + + return NULL; +} + +int pthread_key_delete(pthread_key_t k) { + struct pthread_key *key; + int res; + + // Get key + res = lstget(&key_list, k, (void **)&key); + if (res) { + return res; + } + + lstremove(&key_list, k, 1); + + return 0; +} diff --git a/libiot/pthread/kill.c b/libiot/pthread/kill.c new file mode 100644 index 0000000..1de80ee --- /dev/null +++ b/libiot/pthread/kill.c @@ -0,0 +1,60 @@ +/* + * 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 "_pthread.h" + +#include <errno.h> +#include <signal.h> +#include <sys/_signal.h> + +int pthread_kill(pthread_t thread, int signal) { + if (signal > PTHREAD_NSIG) { + return EINVAL; + } + + _signal_queue(thread, signal); + + return 0; +} diff --git a/libiot/pthread/mkfile b/libiot/pthread/mkfile new file mode 100644 index 0000000..2fc273c --- /dev/null +++ b/libiot/pthread/mkfile @@ -0,0 +1,10 @@ +#<$ROOT/mkconfig + +#LIB=libspiffs.a +OFILES=\ + $OFILES\ + pthread/mutex.$O\ + +#HFILES= $ROOT/include/bio.h + +#<$ROOT/mkfiles/mksyslib-$SHELLTYPE diff --git a/libiot/pthread/mutex.c b/libiot/pthread/mutex.c new file mode 100644 index 0000000..c4850d1 --- /dev/null +++ b/libiot/pthread/mutex.c @@ -0,0 +1,241 @@ +/* + * 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 "_pthread.h" + +//#include "esp_attr.h" + +#include <stdlib.h> + +static int _check_attr(const pthread_mutexattr_t *attr) { + int type = attr->type; + + if ((type < PTHREAD_MUTEX_NORMAL) || (type > PTHREAD_MUTEX_DEFAULT)) { + return EINVAL; + } + + return 0; +} + +int pthread_mutex_init(pthread_mutex_t *mut, const pthread_mutexattr_t *attr) { + struct pthread_mutex *mutex; + int res; + + if (!mut) { + return EINVAL; + } + + // Check attr + if (attr) { + res = _check_attr(attr); + if (res) { + return res; + } + } + + // Test if it's init yet + if (*mut != PTHREAD_MUTEX_INITIALIZER) { + return EBUSY; + } + + // Create mutex structure + mutex = (struct pthread_mutex *)malloc(sizeof(struct pthread_mutex)); + if (!mutex) { + return EINVAL; + } + + if (attr) { + mutex->type = attr->type; + } else { + mutex->type = PTHREAD_MUTEX_NORMAL; + } + // Create semaphore + if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { + mutex->sem = xSemaphoreCreateRecursiveMutex(); + } else { + mutex->sem = xSemaphoreCreateMutex(); + } + if(!mutex->sem){ + *mut = PTHREAD_MUTEX_INITIALIZER; + free(mutex->sem); + free(mutex); + return ENOMEM; + } + + mutex->owner = pthread_self(); + + *mut = (unsigned int )mutex; + + return 0; +} + +int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mut) { + struct pthread_mutex *mutex; + int res; + + if (!mut) { + return EINVAL; + } + + if ((intptr_t) *mut == PTHREAD_MUTEX_INITIALIZER) { + if ((res = pthread_mutex_init(mut, NULL))) { + return res; + } + } + + mutex = (struct pthread_mutex *)(*mut); + + // Lock + if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { + if (xSemaphoreTakeRecursive(mutex->sem, PTHREAD_MTX_LOCK_TIMEOUT) != pdPASS) { + PTHREAD_MTX_DEBUG_LOCK(); + return EINVAL; + } + } else { + if (xSemaphoreTake(mutex->sem, PTHREAD_MTX_LOCK_TIMEOUT) != pdPASS) { + PTHREAD_MTX_DEBUG_LOCK(); + return EINVAL; + } + } + + return 0; +} + +int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mut) { + if (!mut) { + return EINVAL; + } + + struct pthread_mutex *mutex = ( struct pthread_mutex *)(*mut); + + // Unlock + if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { + xSemaphoreGiveRecursive(mutex->sem); + } else { + xSemaphoreGive(mutex->sem); + } + + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *mut) { + struct pthread_mutex *mutex; + int res; + + if (!mut) { + return EINVAL; + } + + if ((intptr_t) *mut == PTHREAD_MUTEX_INITIALIZER) { + if ((res = pthread_mutex_init(mut, NULL))) { + return res; + } + } + + mutex = ( struct pthread_mutex *)(*mut); + + // Try lock + if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { + if (xSemaphoreTakeRecursive(mutex->sem,0 ) != pdTRUE) { + return EBUSY; + } + } else { + if (xSemaphoreTake(mutex->sem,0 ) != pdTRUE) { + return EBUSY; + } + } + + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mut) { + if (!mut) { + return EINVAL; + } + + struct pthread_mutex *mutex = ( struct pthread_mutex *)(*mut); + + if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { + xSemaphoreGiveRecursive(mutex->sem); + } else { + xSemaphoreGive(mutex->sem); + } + + vSemaphoreDelete(mutex->sem); + + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { + pthread_mutexattr_t temp_attr; + int res; + + // Check attr + if (!attr) { + return EINVAL; + } + + temp_attr.type = type; + + res = _check_attr(&temp_attr); + if (res) { + return res; + } + + attr->type = type; + + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { + if (!attr) { + return EINVAL; + } + + attr->type = PTHREAD_MUTEX_NORMAL; + attr->is_initialized = 1; + + return 0; +} diff --git a/libiot/pthread/once.c b/libiot/pthread/once.c new file mode 100644 index 0000000..646de27 --- /dev/null +++ b/libiot/pthread/once.c @@ -0,0 +1,67 @@ +/* + * 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 "_pthread.h" + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { + if (!once_control || !init_routine) { + return EINVAL; + } + + if (once_control->is_initialized != 1) { + return EINVAL; + } + + _pthread_lock(); + if (!once_control->init_executed) { + once_control->init_executed = 1; + _pthread_unlock(); + init_routine(); + } else { + _pthread_unlock(); + } + + return 0; +} diff --git a/libiot/pthread/self.c b/libiot/pthread/self.c new file mode 100644 index 0000000..017ac5b --- /dev/null +++ b/libiot/pthread/self.c @@ -0,0 +1,53 @@ +/* + * 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 "_pthread.h" +#include "esp_attr.h" + +extern UBaseType_t uxGetThreadId(); + +pthread_t IRAM_ATTR pthread_self(void) { + return (pthread_t)uxGetThreadId(); +} diff --git a/libiot/pthread/test/pthread-cleanup.c b/libiot/pthread/test/pthread-cleanup.c new file mode 100644 index 0000000..c11aeb4 --- /dev/null +++ b/libiot/pthread/test/pthread-cleanup.c @@ -0,0 +1,200 @@ +#include "unity.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <sys/delay.h> + +#define NUM_TERMS 4 + +static int term[NUM_TERMS]; + +void term_0(void *args) { + term[0] = 0; +} + +void term_1(void *args) { + term[1] = 1; +} + +void term_2(void *args) { + term[2] = 2; +} + +void term_3(void *args) { + term[3] = 3; +} + +// Test that handlers are called in reverse order +static void *thread1(void *args) { + int count = 0; + + memset(term, -1, sizeof(term)); + + pthread_cleanup_push(term_0, NULL); + pthread_cleanup_push(term_1, NULL); + pthread_cleanup_push(term_2, NULL); + pthread_cleanup_push(term_3, NULL); + + for (count = 0; count < 100; count++) { + // Simulate some work + usleep(1000); + } + + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + + TEST_ASSERT(term[0] == 0); + TEST_ASSERT(term[1] == 1); + TEST_ASSERT(term[2] == 2); + TEST_ASSERT(term[3] == 3); + + return NULL; +} + +// Test that only handlers poped with execute argument set to true are +// executed +static void *thread2(void *args) { + int count = 0; + + memset(term, -1, sizeof(term)); + + pthread_cleanup_push(term_0, NULL); + pthread_cleanup_push(term_1, NULL); + pthread_cleanup_push(term_2, NULL); + pthread_cleanup_push(term_3, NULL); + + for (count = 0; count < 100; count++) { + // Simulate some work + usleep(1000); + } + + pthread_cleanup_pop(0); + pthread_cleanup_pop(1); + pthread_cleanup_pop(0); + pthread_cleanup_pop(1); + + TEST_ASSERT(term[0] == 0); + TEST_ASSERT(term[1] == -1); + TEST_ASSERT(term[2] == 2); + TEST_ASSERT(term[3] == -1); + + pthread_exit(NULL); +} + +// Test that pthread_exit execute the cleanups +static void *thread3(void *args) { + int count = 0; + + memset(term, -1, sizeof(term)); + + pthread_cleanup_push(term_0, NULL); + pthread_cleanup_push(term_1, NULL); + pthread_cleanup_push(term_2, NULL); + pthread_cleanup_push(term_3, NULL); + + for (count = 0; count < 100; count++) { + // Simulate some work + usleep(1000); + + if (count == 50) { + pthread_exit(NULL); + } + } + + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + + // This point is never reached + TEST_ASSERT(0); + + pthread_exit(NULL); +} + +// Test that when exit from thread with return don't execute the cleanups +static void *thread4(void *args) { + int count = 0; + + memset(term, -1, sizeof(term)); + + pthread_cleanup_push(term_0, NULL); + pthread_cleanup_push(term_1, NULL); + pthread_cleanup_push(term_2, NULL); + pthread_cleanup_push(term_3, NULL); + + for (count = 0; count < 100; count++) { + // Simulate some work + usleep(1000); + + if (count == 50) { + return NULL; + } + } + + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + + // This point is never reached + TEST_ASSERT(0); + + return NULL; +} + +TEST_CASE("pthread cleanup", "[pthread]") { + pthread_attr_t attr; + pthread_t th; + int ret; + + pthread_attr_init(&attr); + + // Test that handlers are called in reverse order + ret = pthread_create(&th, &attr, thread1, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_join(th, NULL); + TEST_ASSERT(ret == 0); + + // Test that only handlers poped with execute argument set to true are + // executed + ret = pthread_create(&th, &attr, thread2, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_join(th, NULL); + TEST_ASSERT(ret == 0); + + // Test that pthread_exit execute the cleanups + ret = pthread_create(&th, &attr, thread3, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_join(th, NULL); + TEST_ASSERT(ret == 0); + + TEST_ASSERT(term[0] == 0); + TEST_ASSERT(term[1] == 1); + TEST_ASSERT(term[2] == 2); + TEST_ASSERT(term[3] == 3); + + // Test that when exit from thread with return don't execute the cleanups + ret = pthread_create(&th, &attr, thread4, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_join(th, NULL); + TEST_ASSERT(ret == 0); + + TEST_ASSERT(term[0] == -1); + TEST_ASSERT(term[1] == -1); + TEST_ASSERT(term[2] == -1); + TEST_ASSERT(term[3] == -1); + + ret = pthread_attr_destroy(&attr); + TEST_ASSERT(ret == 0); +} diff --git a/libiot/pthread/test/pthread-cond.c b/libiot/pthread/test/pthread-cond.c new file mode 100644 index 0000000..8e1ce41 --- /dev/null +++ b/libiot/pthread/test/pthread-cond.c @@ -0,0 +1,107 @@ +#include "unity.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <pthread.h> + +#include <sys/delay.h> + +#define NUM_THREADS 3 +#define COUNT_LIMIT 12 +#define TCOUNT 10 + +static int count = 0; +static pthread_t threads[NUM_THREADS]; +static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t count_threshold_cv = PTHREAD_COND_INITIALIZER; + +static void *inc_count(void *args) { + int i; + int ret; + + for (i=0; i< TCOUNT; i++) { + ret = pthread_mutex_lock(&count_mutex); + TEST_ASSERT(ret == 0); + + count++; + + // If condition is reached signal condition + if (count == COUNT_LIMIT) { + ret = pthread_cond_signal(&count_threshold_cv); + TEST_ASSERT(ret == 0); + } + + ret = pthread_mutex_unlock(&count_mutex); + TEST_ASSERT(ret == 0); + + // Simulate some work + usleep(1000); + } + + return NULL; + } + +static void *watch_count(void *args) { + int ret; + + ret = pthread_mutex_lock(&count_mutex); + TEST_ASSERT(ret == 0); + + while (count < COUNT_LIMIT) { + ret = pthread_cond_wait(&count_threshold_cv, &count_mutex); + TEST_ASSERT(ret == 0); + } + + count += 125; + TEST_ASSERT (count == 125 + COUNT_LIMIT); + + ret = pthread_mutex_unlock(&count_mutex); + TEST_ASSERT(ret == 0); + + return NULL; +} + +TEST_CASE("pthread conditions", "[pthread]") { + pthread_attr_t attr; + int i, ret; + + // Initialize mutex and condition variable objects + ret = pthread_mutex_init(&count_mutex, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_cond_init(&count_threshold_cv, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_attr_init(&attr); + TEST_ASSERT(ret == 0); + + ret = pthread_attr_setstacksize(&attr, 10240); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[0], &attr, watch_count, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[1], &attr, inc_count, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[2], &attr, inc_count, NULL); + TEST_ASSERT(ret == 0); + + // Wait for all threads completion + for (i=0; i< NUM_THREADS; i++) { + ret = pthread_join(threads[i], NULL); + TEST_ASSERT(ret == 0); + } + + // Clean up + ret = pthread_attr_destroy(&attr); + TEST_ASSERT(ret == 0); + + ret = pthread_mutex_destroy(&count_mutex); + TEST_ASSERT(ret == 0); + + ret = pthread_cond_destroy(&count_threshold_cv); + TEST_ASSERT(ret == 0); +} diff --git a/libiot/pthread/test/pthread-join.c b/libiot/pthread/test/pthread-join.c new file mode 100644 index 0000000..ba5f976 --- /dev/null +++ b/libiot/pthread/test/pthread-join.c @@ -0,0 +1,85 @@ +#include "unity.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <pthread.h> + +#include <sys/delay.h> + +#define NUM_THREADS 2 + +static pthread_t threads[NUM_THREADS]; + +static void *thread1(void *args) { + int count = 0; + + for (count = 0; count < 100; count++) { + // Simulate some work + usleep(1000); + } + + int *ret = malloc(sizeof(int)); + *ret = count; + + pthread_exit(ret); + } + +static void *thread2(void *args) { + int count; + + for (count = 0; count < 50; count++) { + // Simulate some work + usleep(1000); + } + + int *ret = malloc(sizeof(int)); + *ret = count; + + pthread_exit(ret); + } + + +TEST_CASE("pthread join", "[pthread]") { + pthread_attr_t attr; + int i, ret; + int *res; + + ret = pthread_attr_init(&attr); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[0], &attr, thread1, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[1], &attr, thread2, NULL); + TEST_ASSERT(ret == 0); + + // Wait for all threads completion + for (i=0; i< NUM_THREADS; i++) { + ret = pthread_join(threads[i], (void **)&res); + TEST_ASSERT(ret == 0); + + if (i == 0) { + TEST_ASSERT(*res == 100); + } else if (i == 1) { + TEST_ASSERT(*res == 50); + } + + free(res); + } + + // Check that a detached thread is not joinable + ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[0], &attr, thread1, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_join(threads[0], (void **)&res); + TEST_ASSERT(ret == EINVAL); + + // Clean up + ret = pthread_attr_destroy(&attr); + TEST_ASSERT(ret == 0); +} diff --git a/libiot/pthread/test/pthread-once.c b/libiot/pthread/test/pthread-once.c new file mode 100644 index 0000000..d3a3db6 --- /dev/null +++ b/libiot/pthread/test/pthread-once.c @@ -0,0 +1,68 @@ +#include "unity.h" + +#include <pthread.h> +#include <sys/delay.h> + +#define NUM_THREADS 2 + +static pthread_t threads[NUM_THREADS]; + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static int execs; + +static void init() { + int ret; + + ret = pthread_mutex_lock(&mutex); + TEST_ASSERT(ret == 0); + + execs++; + + ret = pthread_mutex_unlock(&mutex); + TEST_ASSERT(ret == 0); +} + +static void *thread1(void *args) { + int ret; + + ret = pthread_once(&once, init); + TEST_ASSERT(ret == 0); + + // Simulate some work + usleep(1000); + + pthread_exit(NULL); + } + +TEST_CASE("pthread once", "[pthread]") { + pthread_attr_t attr; + int i, ret; + + ret = pthread_attr_init(&attr); + TEST_ASSERT(ret == 0); + + ret = pthread_mutex_init(&mutex, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[0], &attr, thread1, NULL); + TEST_ASSERT(ret == 0); + + ret = pthread_create(&threads[1], &attr, thread1, NULL); + TEST_ASSERT(ret == 0); + + // Wait for all threads completion + for (i=0; i< NUM_THREADS; i++) { + ret = pthread_join(threads[i], NULL); + TEST_ASSERT(ret == 0); + } + + TEST_ASSERT(execs == 1); + + // Clean up + ret = pthread_attr_destroy(&attr); + TEST_ASSERT(ret == 0); + + ret = pthread_mutex_destroy(&mutex); + TEST_ASSERT(ret == 0); +} |
