aboutsummaryrefslogtreecommitdiff
path: root/libiot/pthread
diff options
context:
space:
mode:
Diffstat (limited to 'libiot/pthread')
-rw-r--r--libiot/pthread/_pthread.c743
-rw-r--r--libiot/pthread/_pthread.h241
-rw-r--r--libiot/pthread/attr.c244
-rw-r--r--libiot/pthread/cond.c204
-rw-r--r--libiot/pthread/create.c55
-rw-r--r--libiot/pthread/join.c70
-rw-r--r--libiot/pthread/key.c163
-rw-r--r--libiot/pthread/kill.c60
-rw-r--r--libiot/pthread/mkfile10
-rw-r--r--libiot/pthread/mutex.c241
-rw-r--r--libiot/pthread/once.c67
-rw-r--r--libiot/pthread/self.c53
-rw-r--r--libiot/pthread/test/pthread-cleanup.c200
-rw-r--r--libiot/pthread/test/pthread-cond.c107
-rw-r--r--libiot/pthread/test/pthread-join.c85
-rw-r--r--libiot/pthread/test/pthread-once.c68
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);
+}