Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/P-p-H-d/mlib.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Pelissier <patrick.pelissier@gmail.com>2023-02-16 02:09:06 +0300
committerPatrick Pelissier <patrick.pelissier@gmail.com>2023-02-16 02:09:06 +0300
commit91f3174a36366dcf0bda37b554300dd42761a1cc (patch)
tree5c19242bfbdab28bf5036e0d3e93445f7fbb1b8b /m-thread.h
parent56f51c59af7d24ad6f7aa53ff1007d798001abbc (diff)
Rename m-mutex header into m-thread
as it is not just about mutex but about thread functions just like the header pthread or thread.h
Diffstat (limited to 'm-thread.h')
-rw-r--r--m-thread.h525
1 files changed, 525 insertions, 0 deletions
diff --git a/m-thread.h b/m-thread.h
new file mode 100644
index 0000000..1e0b2bd
--- /dev/null
+++ b/m-thread.h
@@ -0,0 +1,525 @@
+/*
+ * M*LIB - Thin Mutex & Thread wrapper
+ *
+ * Copyright (c) 2017-2023, Patrick Pelissier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS AND CONTRIBUTORS 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.
+*/
+#ifndef MSTARLIB_MUTEX_H
+#define MSTARLIB_MUTEX_H
+
+/* Auto-detect the thread backend to use if the user has not override it */
+#ifndef M_USE_THREAD_BACKEND
+# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
+ && !defined(__STDC_NO_THREADS__)
+# define M_USE_THREAD_BACKEND 1
+# elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
+# define M_USE_THREAD_BACKEND 2
+# else
+# define M_USE_THREAD_BACKEND 3
+# endif
+#endif
+
+
+/****************************** C11 version ********************************/
+#if M_USE_THREAD_BACKEND == 1
+
+#include <threads.h>
+#include <assert.h>
+#include <stdbool.h>
+#include "m-core.h"
+
+M_BEGIN_PROTECTED_CODE
+
+/* Define a mutex type based on C11 definition */
+typedef mtx_t m_mutex_t[1];
+
+/* Define a condition variable type based on C11 definition */
+typedef cnd_t m_cond_t[1];
+
+/* Define a thread type based on C11 definition */
+typedef thrd_t m_thread_t[1];
+
+/* Initialize the mutex (constructor) */
+static inline void m_mutex_init(m_mutex_t m)
+{
+ int rc = mtx_init(m, mtx_plain);
+ // Abort program in case of initialization failure
+ // There is really nothing else to do if a mutex cannot be constructed
+ M_ASSERT_INIT (rc == thrd_success, "mutex");
+}
+
+/* Clear the mutex (destructor) */
+static inline void m_mutex_clear(m_mutex_t m)
+{
+ mtx_destroy(m);
+}
+
+/* Lock the mutex */
+static inline void m_mutex_lock(m_mutex_t m)
+{
+ mtx_lock(m);
+}
+
+/* Unlock the mutex */
+static inline void m_mutex_unlock(m_mutex_t m)
+{
+ mtx_unlock(m);
+}
+
+/* Initialize the condition variable (constructor) */
+static inline void m_cond_init(m_cond_t c)
+{
+ int rc = cnd_init(c);
+ // Abort program in case of initialization failure
+ // There is really nothing else to do if the object cannot be constructed
+ M_ASSERT_INIT (rc == thrd_success, "conditional variable");
+}
+
+/* Clear the condition variable (destructor) */
+static inline void m_cond_clear(m_cond_t c)
+{
+ cnd_destroy(c);
+}
+
+/* Signal the condition variable to at least one waiting thread */
+static inline void m_cond_signal(m_cond_t c)
+{
+ cnd_signal(c);
+}
+
+/* Signal the condition variable to all waiting threads */
+static inline void m_cond_broadcast(m_cond_t c)
+{
+ cnd_broadcast(c);
+}
+
+/* Wait for signaling the condition variable by another thread */
+static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
+{
+ cnd_wait(c, m);
+}
+
+/* Create the thread (constructor) and start it */
+static inline void m_thread_create(m_thread_t t, void (*func)(void*), void* arg)
+{
+ int rc = thrd_create(t, (int(*)(void*))(void(*)(void))func, arg);
+ // Abort program in case of initialization failure
+ M_ASSERT_INIT (rc == thrd_success, "thread");
+}
+
+/* Wait for the thread to terminate and destroy it (destructor) */
+static inline void m_thread_join(m_thread_t t)
+{
+ int rc = thrd_join(*t, NULL);
+ M_ASSERT (rc == thrd_success);
+ // Avoid warning about variable unused.
+ (void) rc;
+}
+
+/* The thread has nothing meaningfull to do.
+ Inform the OS to let other threads be scheduled */
+static inline void m_thread_yield(void)
+{
+ thrd_yield();
+}
+
+/* Sleep the thread for at least usec microseconds.
+ Return true if the sleep was successful */
+static inline bool m_thread_sleep(unsigned long long usec)
+{
+ struct timespec tv;
+ tv.tv_sec = (long) (usec / 1000000ULL);
+ tv.tv_nsec = (long) ((usec % 1000000ULL) * 1000UL);
+ int retval = thrd_sleep(&tv, NULL);
+ return retval == 0;
+}
+
+// a helper structure for m_once_call
+typedef once_flag m_once_t[1];
+
+// Initial value for m_once_t
+#define M_ONCE_INIT_VALUE { ONCE_FLAG_INIT }
+
+// Call the function exactly once
+static inline void m_once_call(m_once_t o, void (*func)(void))
+{
+ call_once(o,func);
+}
+
+// Attribute to use to allocate a global variable to a thread.
+#define M_THREAD_ATTR _Thread_local
+
+M_END_PROTECTED_CODE
+
+
+/****************************** WIN32 version ******************************/
+#elif M_USE_THREAD_BACKEND == 2
+
+/* CLANG provides some useless and wrong warnings:
+ * - _WIN32_WINNT starts with '_' which is reserved by the standard
+ * as per the MSVC compiler, it is needed to be defined by the user
+ * to define which version of windows it want to be compatible with.
+ * - windows.h may be different than the case used by the file sytem
+ * there is however no normalized case.
+ *
+ * So, theses warnings have to be ignored and are disabled.
+ *
+ * We cannot add theses warnings in M_BEGIN_PROTECTED_CODE
+ * as they need to be disabled **BEFORE** including any system header
+ * and m-core includes some system headers.
+ * So we need to disable them explictly here.
+ */
+#if defined(__clang__) && __clang_major__ >= 4
+ _Pragma("clang diagnostic push")
+ _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
+ _Pragma("clang diagnostic ignored \"-Wnonportable-system-include-path\"")
+#endif
+
+/* CriticalSection & ConditionVariable are available from Windows Vista */
+#ifndef WINVER
+#define WINVER _WIN32_WINNT_VISTA
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#endif
+
+/* Include system headers */
+#include <windows.h>
+#include <assert.h>
+#include <stdbool.h>
+#include "m-core.h"
+
+#if defined(__clang__) && __clang_major__ >= 4
+ _Pragma("clang diagnostic pop")
+#endif
+
+M_BEGIN_PROTECTED_CODE
+
+/* Define a thread type based on WINDOWS definition */
+typedef HANDLE m_thread_t[1];
+
+/* Define a mutex type based on WINDOWS definition */
+typedef CRITICAL_SECTION m_mutex_t[1];
+
+/* Define a condition variable type based on WINDOWS definition */
+typedef CONDITION_VARIABLE m_cond_t[1];
+
+/* Initialize a mutex (Constructor)*/
+static inline void m_mutex_init(m_mutex_t m)
+{
+ InitializeCriticalSection(m);
+}
+
+/* Clear a mutex (destructor) */
+static inline void m_mutex_clear(m_mutex_t m)
+{
+ DeleteCriticalSection(m);
+}
+
+/* Lock a mutex */
+static inline void m_mutex_lock(m_mutex_t m)
+{
+ EnterCriticalSection(m);
+}
+
+/* Unlock a mutex */
+static inline void m_mutex_unlock(m_mutex_t m)
+{
+ LeaveCriticalSection(m);
+}
+
+/* Initialize a condition variable (constructor) */
+static inline void m_cond_init(m_cond_t c)
+{
+ InitializeConditionVariable(c);
+}
+
+/* Clear a condition variable (destructor) */
+static inline void m_cond_clear(m_cond_t c)
+{
+ (void) c; // There is no destructor for this object.
+}
+
+/* Signal a condition variable to at least one waiting thread */
+static inline void m_cond_signal(m_cond_t c)
+{
+ WakeConditionVariable(c);
+}
+
+/* Signal a condition variable to all waiting threads */
+static inline void m_cond_broadcast(m_cond_t c)
+{
+ WakeAllConditionVariable(c);
+}
+
+/* Wait for a condition variable */
+static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
+{
+ SleepConditionVariableCS(c, m, INFINITE);
+}
+
+/* Create a thread (constructor) and start it */
+static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
+{
+ *t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (uintptr_t) func, arg, 0, NULL);
+ M_ASSERT_INIT (*t != NULL, "thread");
+}
+
+/* Wait for the thread to terminate and destroy it (destructor) */
+static inline void m_thread_join(m_thread_t t)
+{
+ DWORD dwWaitResult = WaitForSingleObject(*t, INFINITE);
+ (void) dwWaitResult;
+ M_ASSERT (dwWaitResult == WAIT_OBJECT_0);
+ CloseHandle(*t);
+}
+
+/* The thread has nothing meaningfull to do.
+ Inform the OS to let other threads be scheduled */
+static inline void m_thread_yield(void)
+{
+ Sleep(0);
+}
+
+/* Sleep the thread for at least usec microseconds
+ Return true if the sleep was successful */
+static inline bool m_thread_sleep(unsigned long long usec)
+{
+ LARGE_INTEGER ft;
+ M_ASSERT (usec <= LLONG_MAX);
+ ft.QuadPart = -(10LL*(long long) usec);
+ HANDLE hd = CreateWaitableTimer(NULL, TRUE, NULL);
+ M_ASSERT_INIT (hd != NULL, "timer");
+ SetWaitableTimer(hd, &ft, 0, NULL, NULL, 0);
+ DWORD dwWaitResult = WaitForSingleObject(hd, INFINITE);
+ CloseHandle(hd);
+ return dwWaitResult == WAIT_OBJECT_0;
+}
+
+
+typedef INIT_ONCE m_once_t[1];
+#define M_ONCE_INIT_VALUE { INIT_ONCE_STATIC_INIT }
+static inline BOOL CALLBACK m_once_callback( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext)
+{
+ void (*func)(void);
+ (void) InitOnce;
+ (void) lpContext;
+ func = (void (*)(void))(uintptr_t) Parameter;
+ (*func)();
+ return TRUE;
+}
+static inline void m_once_call(m_once_t o, void (*func)(void))
+{
+ InitOnceExecuteOnce(o, m_once_callback, (void*)(intptr_t)func, NULL);
+}
+
+#if defined(_MSC_VER)
+// Attribute to use to allocate a global variable to a thread (MSVC def).
+# define M_THREAD_ATTR __declspec( thread )
+#else
+// Attribute to use to allocate a global variable to a thread (GCC def).
+# define M_THREAD_ATTR __thread
+#endif
+
+M_END_PROTECTED_CODE
+
+
+/**************************** PTHREAD version ******************************/
+#else
+
+#include <pthread.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+#include <sched.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdbool.h>
+#include "m-core.h"
+
+M_BEGIN_PROTECTED_CODE
+
+/* Define a mutex type based on PTHREAD definition */
+typedef pthread_mutex_t m_mutex_t[1];
+
+/* Define a condition variable type based on PTHREAD definition */
+typedef pthread_cond_t m_cond_t[1];
+
+/* Define a thread type based on PTHREAD definition */
+typedef pthread_t m_thread_t[1];
+
+/* Initialize the mutex (constructor) */
+static inline void m_mutex_init(m_mutex_t m)
+{
+ int _rc = pthread_mutex_init(m, NULL);
+ // Abort program in case of initialization failure
+ // There is really nothing else to do if a mutex cannot be constructed
+ M_ASSERT_INIT (_rc == 0, "mutex");
+}
+
+/* Clear the mutex (destructor) */
+static inline void m_mutex_clear(m_mutex_t m)
+{
+ pthread_mutex_destroy(m);
+}
+
+/* Lock the mutex */
+static inline void m_mutex_lock(m_mutex_t m)
+{
+ pthread_mutex_lock(m);
+}
+
+/* Unlock the mutex */
+static inline void m_mutex_unlock(m_mutex_t m)
+{
+ pthread_mutex_unlock(m);
+}
+
+/* Lazy lock initialization */
+#define M_MUTEXI_INIT_VALUE { PTHREAD_MUTEX_INITIALIZER }
+
+/* Internal function compatible with lazy lock */
+static inline void m_mutexi_lazy_lock(m_mutex_t m)
+{
+ pthread_mutex_lock(m);
+}
+
+/* Initialize the condition variable (constructor) */
+static inline void m_cond_init(m_cond_t c)
+{
+ int _rc = pthread_cond_init(c, NULL);
+ // Abort program in case of initialization failure
+ // There is really nothing else to do if a mutex cannot be constructed
+ M_ASSERT_INIT (_rc == 0, "conditional variable");
+}
+
+/* Clear the condition variable (destructor) */
+static inline void m_cond_clear(m_cond_t c)
+{
+ pthread_cond_destroy(c);
+}
+
+/* Signal a condition variable to at least a waiting thread */
+static inline void m_cond_signal(m_cond_t c)
+{
+ pthread_cond_signal(c);
+}
+
+/* Signal a condition variable to all waiting threads */
+static inline void m_cond_broadcast(m_cond_t c)
+{
+ pthread_cond_broadcast(c);
+}
+
+/* Waiting for a condition variable */
+static inline void m_cond_wait(m_cond_t c, m_mutex_t m)
+{
+ pthread_cond_wait(c, m);
+}
+
+/* Create a thread (constructor) and start it */
+static inline void m_thread_create(m_thread_t t, void (*func)(void*), void *arg)
+{
+ int _rc = pthread_create(t, NULL, (void*(*)(void*))(void(*)(void))func, arg);
+ M_ASSERT_INIT (_rc == 0, "thread");
+}
+
+/* Wait for the thread to terminate and destroy it (destructor) */
+static inline void m_thread_join(m_thread_t t)
+{
+ int _rc = pthread_join(*t, NULL);
+ (void)_rc; // Avoid warning about variable unused.
+ M_ASSERT (_rc == 0);
+}
+
+/* The thread has nothing meaningfull to do.
+ Inform the OS to let other threads be scheduled */
+static inline void m_thread_yield(void)
+{
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ sched_yield();
+#endif
+}
+
+/* Sleep for at least usec microseconds
+ Return true if the sleep was successful */
+static inline bool m_thread_sleep(unsigned long long usec)
+{
+ struct timeval tv;
+ /* We don't want to use usleep or nanosleep so that
+ we remain compatible with strict C99 build */
+ tv.tv_sec = (time_t) (usec / 1000000ULL);
+ tv.tv_usec = (suseconds_t) (usec % 1000000ULL);
+ int retval = select(1, NULL, NULL, NULL, &tv);
+ return retval == 0;
+}
+
+typedef pthread_once_t m_once_t[1];
+#define M_ONCE_INIT_VALUE { PTHREAD_ONCE_INIT }
+static inline void m_once_call(m_once_t o, void (*func)(void))
+{
+ pthread_once(o,func);
+}
+
+#define M_THREAD_ATTR __thread
+
+M_END_PROTECTED_CODE
+
+#endif
+
+// TODO: Obsolete M_LOCK macro.
+
+/* M_LOCK macro. Allow simple locking encapsulation.
+ USAGE:
+ static M_LOCK_DECL(name);
+ int f(int n) {
+ M_LOCK(name) {
+ // Exclusive access
+ }
+ }
+*/
+/* NOTE: Either using direct support by the OS (WIN32/PTHREAD)
+ or using C11's ONCE mechanism */
+#ifdef M_MUTEXI_INIT_VALUE
+# define M_LOCK_DECL(name) m_mutex_t name = M_MUTEXI_INIT_VALUE
+# define M_LOCK(name) \
+ M_LOCKI_DO(name, M_C(local_cont_, __LINE__), m_mutexi_lazy_lock, m_mutex_unlock)
+#else
+# define M_LOCK_DECL(name) \
+ m_mutex_t name; \
+ static void M_C(m_mutex_init_, name)(void) { \
+ m_mutex_init(name); \
+ } \
+ m_once_t M_C(m_once_, name) = M_ONCE_INIT_VALUE
+# define M_LOCKI_BY_ONCE(name) \
+ (m_once_call(M_C(m_once_, name), M_C(m_mutex_init_, name)), \
+ m_mutex_lock(name), (void) 0 )
+# define M_LOCK(name) \
+ M_LOCKI_DO(name, M_C(local_cont_, __LINE__), M_LOCKI_BY_ONCE, m_mutex_unlock)
+#endif
+
+#define M_LOCKI_DO(name, cont, lock_func, unlock_func) \
+ for(bool cont = true \
+ ; cont && (lock_func (name), true); \
+ (unlock_func (name), cont = false))
+
+#endif