commit d1bbf57555b5b43252252e584dd3db3ab792658c
Author: vaplv <vaplv@free.fr>
Date: Wed, 18 Sep 2013 10:23:18 +0200
First commit
Diffstat:
| A | LICENSE | | | 24 | ++++++++++++++++++++++++ |
| A | README.md | | | 4 | ++++ |
| A | src/CMakeLists.txt | | | 87 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/atomic.h | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/clock_time.c | | | 125 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/clock_time.h | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/condition.h | | | 22 | ++++++++++++++++++++++ |
| A | src/image.c | | | 71 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/image.h | | | 23 | +++++++++++++++++++++++ |
| A | src/list.h | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/math.h | | | 32 | ++++++++++++++++++++++++++++++++ |
| A | src/mem_allocator.c | | | 481 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/mem_allocator.h | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/mutex.h | | | 32 | ++++++++++++++++++++++++++++++++ |
| A | src/pthread/condition.h | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/pthread/mutex.h | | | 116 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/ref_count.h | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
| A | src/rsys.h | | | 138 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/signal.h | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_atomic.c | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_condition.c | | | 9 | +++++++++ |
| A | src/test_list.c | | | 193 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_mem_allocator.c | | | 90 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_mutex.c | | | 215 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_ref.c | | | 33 | +++++++++++++++++++++++++++++++++ |
| A | src/test_signal.c | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
26 files changed, 2413 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013 Vincent Forest
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
diff --git a/README.md b/README.md
@@ -0,0 +1,4 @@
+rsys
+====
+
+Minimalist library defining platform specific macros and data structures.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 2.6)
+project(rsys C)
+enable_testing()
+
+if(NOT CMAKE_COMPILER_IS_GNUCC)
+ message(FATAL_ERROR "Unsupported compiler")
+endif(NOT CMAKE_COMPILER_IS_GNUCC)
+
+find_package(Threads)
+find_package(OpenMP)
+
+################################################################################
+# Setup compile flags/parameters
+################################################################################
+set(CMAKE_DEBUG_POSTFIX "-dbg")
+set(CMAKE_C_FLAGS "-pedantic -std=c99 -Wall -Wextra -Wcast-align -Wmissing-declarations -Wmissing-prototypes -fvisibility=hidden -fstrict-aliasing -Wl,-z,defs -Wconversion")
+set(CMAKE_C_FLAGS_DEBUG "-g -DDEBUG")
+set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
+
+if(CMAKE_USE_PTHREADS_INIT)
+ add_definitions(-DRSYS_USE_PTHREADS)
+endif()
+add_definitions(-D_POSIX_C_SOURCE=200112L)
+
+################################################################################
+# Define targets
+################################################################################
+set(RSYS_FILES_SRC
+ clock_time.c
+#image.c
+ mem_allocator.c)
+
+set(RSYS_FILES_INC
+ atomic.h
+ clock_time.h
+# image.h
+ list.h
+# math.h
+ mem_allocator.h
+# ref_count.h
+# signal.h
+ rsys.h)
+
+add_library(rsys SHARED ${RSYS_FILES_SRC} ${RSYS_FILES_INC})
+set_target_properties(rsys PROPERTIES DEFINE_SYMBOL RSYS_SHARED_BUILD)
+
+################################################################################
+# Add tests
+################################################################################
+add_executable(test_list test_list.c)
+target_link_libraries(test_list rsys)
+add_test(test_list test_list)
+
+add_executable(test_mem_allocator test_mem_allocator.c)
+target_link_libraries(test_mem_allocator rsys)
+add_test(test_mem_allocator test_mem_allocator)
+
+add_executable(test_atomic test_atomic.c)
+add_test(test_atomic test_atomic)
+
+add_executable(test_ref test_ref.c)
+add_test(test_ref test_ref)
+
+if(NOT OPENMP_FOUND)
+ message(STATUS "No OpenMP support: multi-threaded tests cannot be generated")
+else()
+ add_executable(test_mutex test_mutex.c)
+ set_target_properties(test_mutex PROPERTIES COMPILE_FLAGS ${OpenMP_C_FLAGS})
+ set_target_properties(test_mutex PROPERTIES LINK_FLAGS ${OpenMP_C_FLAGS})
+ target_link_libraries(test_mutex rsys)
+ add_test(test_mutex test_mutex)
+
+ add_executable(test_condition test_condition.c)
+ set_target_properties(test_condition PROPERTIES COMPILE_FLAGS ${OpenMP_C_FLAGS})
+ set_target_properties(test_condition PROPERTIES LINK_FLAGS ${OpenMP_C_FLAGS})
+ add_test(test_condition test_condition)
+endif()
+
+#add_executable(test_signal test_signal.c)
+#target_link_libraries(test_signal snlsys)
+#add_test(test_signal test_signal)
+
+################################################################################
+# Define output & install directories
+################################################################################
+#install(TARGETS rsys LIBRARY DESTINATION lib)
+install(FILES ${RSYS_FILES_INC} DESTINATION include/rsys)
diff --git a/src/atomic.h b/src/atomic.h
@@ -0,0 +1,56 @@
+#ifndef ATOMIC_H
+#define ATOMIC_H
+
+#include "rsys.h"
+
+/*******************************************************************************
+ * GCC implementation
+ ******************************************************************************/
+#ifdef COMPILER_GCC
+
+typedef int atomic_int_T;
+typedef size_t atomic_size_T;
+
+#define ATOMIC_INCR(Atom) __sync_add_and_fetch((Atom), 1)
+#define ATOMIC_DECR(Atom) __sync_sub_and_fetch((Atom), 1)
+#define ATOMIC_ADD(Atom, Val) __sync_add_and_fetch((Atom), (Val))
+#define ATOMIC_SUB(Atom, Val) __sync_sub_and_fetch((Atom), (Val))
+#define ATOMIC_FETCH_AND_INCR(Atom) __sync_fetch_and_add((Atom), 1)
+#define ATOMIC_FETCH_AND_DECR(Atom) __sync_fetch_and_sub((Atom), 1)
+#define ATOMIC_FETCH_AND_ADD(Atom, Val) __sync_fetch_and_add((Atom), (Val))
+#define ATOMIC_FETCH_AND_SUB(Atom, Val) __sync_fetch_and_sub((Atom), (Val))
+#define ATOMIC_CMP_AND_SWAP(Atom, NewVal, Comparand) \
+ __sync_val_compare_and_swap(Atom, Comparand, NewVal)
+
+/*******************************************************************************
+ * Other implementation
+ ******************************************************************************/
+#else
+typedef int atomic_int_T;
+typedef size_t atomic_size_T;
+
+#define ATOMIC_INCR(Atom) ASSERT(0), (void)0
+#define ATOMIC_DECR(Atom) ASSERT(0), (void)0
+#define ATOMIC_ADD(Atom, Val) ASSERT(0), (void)0
+#define ATOMIC_SUB(Atom, Val) ASSERT(0), (void)0
+#define ATOMIC_FETCH_AND_INCR(Atom) ASSERT(0), (void)0
+#define ATOMIC_FETCH_AND_DECR(Atom) ASSERT(0), (void)0
+#define ATOMIC_FETCH_AND_ADD(Atom, Val) ASSERT(0), (void)0
+#define ATOMIC_FETCH_AND_SUB(Atom, Val) ASSERT(0), (void)0
+#define ATOMIC_CMP_AND_SWAP(Atom, NewVal, Comparand) ASSERT(0), (void)0
+
+#endif /* COMPILER_XXX */
+
+/*******************************************************************************
+ * Generic implementation
+ ******************************************************************************/
+#define ATOMIC_FETCH_AND_STORE(Atom, Val, Res) \
+ for(;;) { \
+ *Res = *Atom; \
+ if(ATOMIC_CMP_AND_SWAP(Atom, Val, *Res) == *Res) \
+ break; \
+ /* TODO add a yield/pause operation */ \
+ }
+
+#endif /* ATOMIC_H */
+
diff --git a/src/clock_time.c b/src/clock_time.c
@@ -0,0 +1,125 @@
+#include "clock_time.h"
+#include <inttypes.h>
+#include <string.h>
+
+#define NSEC_PER_USEC 1000L
+#define NSEC_PER_MSEC (1000L * NSEC_PER_USEC)
+#define NSEC_PER_SEC (1000L * NSEC_PER_MSEC)
+#define NSEC_PER_MIN (60L * NSEC_PER_SEC)
+#define NSEC_PER_HOUR (60L * NSEC_PER_MIN)
+#define NSEC_PER_DAY (24L * NSEC_PER_HOUR)
+
+int64_t
+time_val(const time_T* time, enum time_unit unit)
+{
+ int64_t val = TIME_TO_NSEC__(time);
+ switch(unit) {
+ case TIME_NSEC:
+ /* Do nothing. */
+ break;
+ case TIME_USEC:
+ val /= NSEC_PER_USEC;
+ break;
+ case TIME_MSEC:
+ val /= NSEC_PER_MSEC;
+ break;
+ case TIME_SEC:
+ val /= NSEC_PER_SEC;
+ break;
+ case TIME_MIN:
+ val /= NSEC_PER_MIN;
+ break;
+ case TIME_HOUR:
+ val /= NSEC_PER_HOUR;
+ break;
+ case TIME_DAY:
+ val /= NSEC_PER_DAY;
+ break;
+ default: ASSERT(0); break;
+ }
+ return val;
+}
+
+void
+time_dump
+ (const time_T* time,
+ int flag,
+ size_t* real_dump_len,
+ char* dump,
+ size_t max_dump_len)
+{
+ size_t available_dump_space = max_dump_len ? max_dump_len - 1 : 0;
+ int64_t time_nsec = 0;
+
+ ASSERT(time && (!max_dump_len || dump));
+
+ #define DUMP(time, suffix) \
+ { \
+ const int len = snprintf \
+ (dump, available_dump_space, \
+ "%" PRIi64 " %s",time, time > 1 ? suffix "s ": suffix " "); \
+ ASSERT(len >= 0); \
+ if(real_dump_len) { \
+ real_dump_len += len; \
+ } \
+ if((size_t)len < available_dump_space) { \
+ dump += len; \
+ available_dump_space -= (size_t)len; \
+ } else if(dump) { \
+ dump[available_dump_space] = '\0'; \
+ available_dump_space = 0; \
+ dump = NULL; \
+ } \
+ } (void) 0
+
+ time_nsec = TIME_TO_NSEC__(time);
+ if(flag & TIME_DAY) {
+ const int64_t nb_days = time_nsec / NSEC_PER_DAY;
+ DUMP(nb_days, "day");
+ time_nsec -= nb_days * NSEC_PER_DAY;
+ }
+ if(flag & TIME_HOUR) {
+ const int64_t nb_hours = time_nsec / NSEC_PER_HOUR;
+ DUMP(nb_hours, "hour");
+ time_nsec -= nb_hours * NSEC_PER_HOUR;
+ }
+ if(flag & TIME_MIN) {
+ const int64_t nb_mins = time_nsec / NSEC_PER_MIN;
+ DUMP(nb_mins, "min");
+ time_nsec -= nb_mins * NSEC_PER_MIN;
+ }
+ if(flag & TIME_SEC) {
+ const int64_t nb_secs = time_nsec / NSEC_PER_SEC;
+ DUMP(nb_secs, "sec");
+ time_nsec -= nb_secs * NSEC_PER_SEC;
+ }
+ if(flag & TIME_MSEC) {
+ const int64_t nb_msecs = time_nsec / NSEC_PER_MSEC;
+ DUMP(nb_msecs, "msec");
+ time_nsec -= nb_msecs * NSEC_PER_MSEC;
+ }
+ if(flag & TIME_USEC) {
+ const int64_t nb_usecs = time_nsec / NSEC_PER_USEC;
+ DUMP(nb_usecs, "usec");
+ time_nsec -= nb_usecs * NSEC_PER_USEC;
+ }
+ if(flag & TIME_NSEC)
+ DUMP(time_nsec, "nsec");
+
+ #undef DUMP
+
+ if(dump) {
+ /* Remove last space. */
+ const size_t last_char = strlen(dump) - 1;
+ ASSERT(dump[last_char] == ' ');
+ dump[last_char] = '\0';
+ }
+}
+
+#undef NSEC_PER_USEC
+#undef NSEC_PER_MSEC
+#undef NSEC_PER_SEC
+#undef NSEC_PER_MIN
+#undef NSEC_PER_HOUR
+#undef NSEC_PER_DAY
+
diff --git a/src/clock_time.h b/src/clock_time.h
@@ -0,0 +1,103 @@
+#ifndef TIME_H
+#define TIME_H
+
+#include "rsys.h"
+
+#ifndef PLATFORM_UNIX
+ #error "Unsupported platform"
+#endif
+
+#if _POSIX_C_SOURCE < 200112L
+ #include <sys/time.h>
+
+ #define CURRENT_TIME__(Time) gettimeofday((Time), NULL)
+ #define GREATER_TIME_UNIT__(Time) (Time)->tv_sec
+ #define SMALLER_TIME_UNIT__(Time) (Time)->tv_usec
+ #define GREATER_TO_SMALLER_TIME_UNIT__ 1000000L
+ #define TIME_TO_NSEC__(Time) \
+ (((Time)->tv_usec + (Time)->tv_sec * 1000000L) * 1000L)
+
+ typedef struct timeval time_T;
+#else
+ #include <time.h>
+
+ #define CURRENT_TIME__(Time) clock_gettime(CLOCK_REALTIME, (Time))
+ #define GREATER_TIME_UNIT__(Time) (Time)->tv_sec
+ #define SMALLER_TIME_UNIT__(Time) (Time)->tv_nsec
+ #define GREATER_TO_SMALLER_TIME_UNIT__ 1000000000L
+ #define TIME_TO_NSEC__(Time) \
+ ((time)->tv_nsec + (Time)->tv_sec * 1000000000L)
+
+ typedef struct timespec time_T;
+#endif
+
+#include <stddef.h>
+
+enum time_unit {
+ TIME_NSEC = BIT(0),
+ TIME_USEC = BIT(1),
+ TIME_MSEC = BIT(2),
+ TIME_SEC = BIT(3),
+ TIME_MIN = BIT(4),
+ TIME_HOUR = BIT(5),
+ TIME_DAY = BIT(6)
+};
+
+static FINLINE void
+time_current(time_T* time)
+{
+ int err = 0; (void) err;
+ ASSERT(time);
+ err = CURRENT_TIME__(time);
+ ASSERT(err == 0);
+
+}
+
+static FINLINE void
+time_sub(time_T* res, const time_T* a, const time_T* b)
+{
+ ASSERT(res && a && b);
+ GREATER_TIME_UNIT__(res) = GREATER_TIME_UNIT__(a) - GREATER_TIME_UNIT__(b);
+ SMALLER_TIME_UNIT__(res) = SMALLER_TIME_UNIT__(a) - SMALLER_TIME_UNIT__(b);
+ if(SMALLER_TIME_UNIT__(res) < 0) {
+ --GREATER_TIME_UNIT__(res);
+ SMALLER_TIME_UNIT__(res) += GREATER_TO_SMALLER_TIME_UNIT__;
+ }
+}
+
+static FINLINE void
+time_add(time_T* res, const time_T* a, const time_T* b)
+{
+ ASSERT(res && a && b);
+
+ GREATER_TIME_UNIT__(res) = GREATER_TIME_UNIT__(a) + GREATER_TIME_UNIT__(b);
+ SMALLER_TIME_UNIT__(res) = SMALLER_TIME_UNIT__(a) + SMALLER_TIME_UNIT__(b);
+ if(SMALLER_TIME_UNIT__(res) >= GREATER_TO_SMALLER_TIME_UNIT__) {
+ ++GREATER_TIME_UNIT__(res);
+ SMALLER_TIME_UNIT__(res) -= GREATER_TO_SMALLER_TIME_UNIT__;
+ }
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+RSYS_API int64_t
+time_val
+ (const time_T* time,
+ enum time_unit unit);
+
+RSYS_API void
+time_dump
+ (const time_T* time,
+ int flag,
+ size_t* real_dump_len, /* May be NULL. */
+ char* dump, /* May be NULL. */
+ size_t max_dump_len);
+
+#ifdef __cplusplus
+} /* extern C */
+#endif
+
+#endif /* TIME_H */
+
diff --git a/src/condition.h b/src/condition.h
@@ -0,0 +1,22 @@
+#ifndef CONDITION_H
+#define CONDITION_H
+
+#include "rsys.h"
+#include "mutex.h"
+
+struct cond;
+
+static FINLINE void cond_init(struct cond* cond);
+static FINLINE void cond_destroy(struct cond* cond);
+static FINLINE void cond_wait(struct cond* cond, struct mutex* mutex);
+static FINLINE void cond_signal(struct cond* cond);
+static FINLINE void cond_broadcast(struct cond* cond);
+
+#ifdef RSYS_USE_PTHREADS
+ #include "pthread/condition.h"
+#else
+ #error "No supported thread library is defined"
+#endif
+
+#endif /* CONDITION_H */
+
diff --git a/src/image.c b/src/image.c
@@ -0,0 +1,71 @@
+#include "image.h"
+#include <stdio.h>
+#include <string.h>
+
+int
+image_ppm_write
+ (const char* path,
+ const int width,
+ const int height,
+ const int Bpp,
+ const unsigned char* buffer)
+{
+ char buf[BUFSIZ];
+ FILE* fp = NULL;
+ int err = 0;
+
+ if(width && height && Bpp && !buffer) {
+ goto error;
+ }
+ fp = fopen(path, "w");
+ if(NULL == fp) {
+ goto error;
+ }
+
+ #define FWRITE(fp, string) \
+ { \
+ const size_t i = fwrite(string, sizeof(char), strlen(string), fp); \
+ if( i != strlen(string) * sizeof(char) ) { \
+ goto error; \
+ } \
+ } (void)0
+ #define SNPRINTF(b, sz, ...) \
+ { \
+ UNUSED const int i = snprintf(b, sz, __VA_ARGS__); \
+ if( i >= BUFSIZ ) { \
+ goto error; \
+ } \
+ } (void)0
+
+ SNPRINTF(buf, BUFSIZ, "%s\n%i %i\n%i\n", "P3\n", width, height, 255);
+ FWRITE(fp, buf);
+
+ if(Bpp) {
+ const long pitch = width * Bpp;
+ int x, y;
+ for(y = 0; y < height; ++y) {
+ const unsigned char* row = buffer + y * pitch;
+ for(x = 0; x < width; ++x) {
+ const unsigned char* pixel = row + x * Bpp;
+ SNPRINTF
+ (buf, BUFSIZ,
+ "%u %u %u\n",
+ pixel[0],
+ Bpp > 1 ? pixel[1] : pixel[0],
+ Bpp > 2 ? pixel[2] : pixel[0]);
+ FWRITE(fp, buf);
+ }
+ FWRITE(fp, "\n");
+ }
+ }
+ #undef SNPRINTF
+ #undef FWRITE
+exit:
+ if(fp)
+ fclose(fp);
+ return err;
+error:
+ goto exit;
+}
+
+
diff --git a/src/image.h b/src/image.h
@@ -0,0 +1,23 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include "snlsys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SNLSYS_API int
+image_ppm_write
+ (const char* path,
+ int width,
+ int height,
+ int bytes_per_pixel,
+ const unsigned char* buffer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* IMAGE_H */
+
diff --git a/src/list.h b/src/list.h
@@ -0,0 +1,127 @@
+#ifndef LIST_H
+#define LIST_H
+
+#include "rsys.h"
+
+struct list_node {
+ struct list_node* next;
+ struct list_node* prev;
+};
+
+/******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static FINLINE void
+add_node__
+ (struct list_node* node,
+ struct list_node* prev,
+ struct list_node* next)
+{
+ ASSERT(node && prev && next);
+ next->prev = node;
+ node->next = next;
+ node->prev = prev;
+ prev->next = node;
+}
+
+static FINLINE void
+del_node__(struct list_node* prev, struct list_node* next)
+{
+ ASSERT(prev && next);
+ next->prev = prev;
+ prev->next = next;
+}
+
+/******************************************************************************
+ * Helper macros
+ ******************************************************************************/
+#define LIST_FOR_EACH(Pos, List) \
+ for(struct list_node* Pos = (List)->next; Pos != (List); Pos = Pos->next)
+
+#define LIST_FOR_EACH_REVERSE(Pos, List) \
+ for(struct list_node* Pos = (List)->prev; Pos != (List); Pos = Pos->prev)
+
+/* Safe against removal of list entry. */
+#define LIST_FOR_EACH_SAFE(Pos, List) \
+ for(struct list_node* Pos = (List)->next,* tmp ## COUNTER ## __ = Pos->next; \
+ Pos != (List); \
+ Pos = tmp ## COUNTER ## __ , tmp ## COUNTER ## __ = Pos->next)
+
+/* Safe against removal of list entry. */
+#define LIST_FOR_EACH_REVERSE_SAFE(Pos, List) \
+ for(struct list_node* Pos = (List)->prev,* tmp ## COUNTER ## __ = Pos->prev; \
+ Pos != (List); \
+ Pos = tmp ## COUNTER ## __, tmp ## COUNTER ## __ = Pos->prev)
+
+/******************************************************************************
+ * Node list functions
+ ******************************************************************************/
+static FINLINE void
+list_init(struct list_node* node)
+{
+ ASSERT(node);
+ node->next = node;
+ node->prev = node;
+}
+
+static FINLINE int
+is_list_empty(const struct list_node* node)
+{
+ ASSERT(node);
+ return node->next == node;
+}
+
+static FINLINE struct list_node*
+list_head(struct list_node* node)
+{
+ ASSERT(node && !is_list_empty(node));
+ return node->next;
+}
+
+static FINLINE struct list_node*
+list_tail(struct list_node* node)
+{
+ ASSERT(node && !is_list_empty(node));
+ return node->prev;
+}
+
+static FINLINE void
+list_add(struct list_node* list, struct list_node* node)
+{
+ ASSERT(list && node && is_list_empty(node));
+ add_node__(node, list, list->next);
+}
+
+static FINLINE void
+list_add_tail(struct list_node* list, struct list_node* node)
+{
+ ASSERT(list && node && is_list_empty(node));
+ add_node__(node, list->prev, list);
+}
+
+static FINLINE void
+list_del(struct list_node* node)
+{
+ ASSERT(node);
+ del_node__(node->prev, node->next);
+ list_init(node);
+}
+
+static FINLINE void
+list_move(struct list_node* node, struct list_node* list)
+{
+ ASSERT(node && list);
+ del_node__(node->prev, node->next);
+ add_node__(node, list, list->next);
+}
+
+static FINLINE void
+list_move_tail(struct list_node* node, struct list_node* list)
+{
+ ASSERT(node && list);
+ del_node__(node->prev, node->next);
+ add_node__(node, list->prev, list);
+}
+
+#endif /* LIST_H */
+
diff --git a/src/math.h b/src/math.h
@@ -0,0 +1,32 @@
+#ifndef SYS_MATH_H
+#define SYS_MATH_H
+
+#define PI 3.14159265358979323846
+
+#define DEG2RAD(x) \
+ ((x)*0.0174532925199432957692369076848861L)
+
+#define RAD2DEG(x) \
+ ((x)*57.2957795130823208767981548141051703L)
+
+#define IS_POWER_OF_2(i) \
+ ((i) > 0 && ((i) & ((i)-1)) == 0)
+
+#define NEXT_POWER_OF_2(i, j) \
+ (j) = (i > 0) ? (i) - 1 : (i), \
+ (j) |= (j) >> 1, \
+ (j) |= (j) >> 2, \
+ (j) |= (j) >> 4, \
+ (j) |= (j) >> 8, \
+ (sizeof(i) > 2 ? (j) |= (j) >> 16, (void)0 : (void)0), \
+ (sizeof(i) > 8 ? (j) |= (j) >> 32, (void)0 : (void)0), \
+ ++(j)
+
+#define MAX(a, b) \
+ ((a) > (b) ? (a) : (b))
+
+#define MIN(a, b) \
+ ((a) < (b) ? (a) : (b))
+
+#endif /* SYS_MATH_H */
+
diff --git a/src/mem_allocator.c b/src/mem_allocator.c
@@ -0,0 +1,481 @@
+#include "atomic.h"
+#include "mem_allocator.h"
+#include "math.h"
+#include <malloc.h>
+#include <string.h>
+
+/*******************************************************************************
+ * Default allocator functions
+ ******************************************************************************/
+struct alloc_counter {
+ atomic_size_T nb_allocs;
+ atomic_size_T allocated_size;
+};
+
+static void*
+default_alloc
+ (void* data,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ void* mem = NULL;
+
+ (void)filename;
+ (void)fileline;
+
+ if(size) {
+ mem = malloc(size);
+ #ifdef NDEBUG
+ (void)data;
+ #else
+ ASSERT(data);
+ if(mem) {
+ struct alloc_counter* counter = data;
+ const size_t size_mem = malloc_usable_size(mem);
+ ATOMIC_ADD(&counter->allocated_size, size_mem);
+ ATOMIC_INCR(&counter->nb_allocs);
+ }
+ #endif
+ }
+ return mem;
+}
+
+static void
+default_free(void* data, void* mem)
+{
+ if(mem) {
+ #ifdef NDEBUG
+ (void)data;
+ #else
+ struct alloc_counter* counter = data;
+ size_t size_mem = malloc_usable_size(mem);
+ ASSERT
+ ( (data != NULL)
+ & (counter->nb_allocs != 0)
+ & (counter->allocated_size >= size_mem));
+
+ ATOMIC_SUB(&counter->allocated_size, size_mem);
+ ATOMIC_DECR(&counter->nb_allocs);
+ #endif
+ free(mem);
+ }
+}
+
+static void*
+default_calloc
+ (void* data,
+ size_t nbelmts,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ void* mem = NULL;
+ const size_t alloc_size = nbelmts * size;
+
+ mem = default_alloc(data, alloc_size, filename, fileline);
+ if(mem) {
+ memset(mem, 0, alloc_size);
+ }
+ return mem;
+}
+
+static void*
+default_realloc
+ (void* data,
+ void* mem,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ void* new_mem = NULL;
+
+ #ifdef NDEBUG
+ (void)data;
+ (void)filename;
+ (void)fileline;
+ new_mem = realloc(mem, size);
+ #else
+ ASSERT(data);
+ if(!mem) {
+ new_mem = default_alloc(data, size, filename, fileline);
+ } else {
+ if(size == 0) {
+ default_free(data, mem);
+ } else {
+ struct alloc_counter* counter = data;
+ const size_t size_old = malloc_usable_size(mem);
+
+ ASSERT(counter->allocated_size >= size_old);
+ ATOMIC_SUB(&counter->allocated_size, size_old);
+
+ new_mem = realloc(mem, size);
+ const size_t size_new = malloc_usable_size(new_mem);
+ ATOMIC_ADD(&counter->allocated_size, size_new);
+ }
+ }
+ #endif
+ return new_mem;
+}
+
+static void*
+default_aligned_alloc
+ (void* data,
+ size_t size,
+ size_t alignment,
+ const char* filename,
+ unsigned int fileline)
+{
+ void* mem = NULL;
+
+ (void)filename;
+ (void)fileline;
+
+ if(size && IS_POWER_OF_2(alignment)) {
+ mem = memalign(alignment, size);
+ #ifdef NDEBUG
+ (void)data;
+ #else
+ ASSERT(data);
+ if(mem) {
+ struct alloc_counter* counter = data;
+ const size_t size_mem = malloc_usable_size(mem);
+ ATOMIC_ADD(&counter->allocated_size, size_mem);
+ ATOMIC_INCR(&counter->nb_allocs);
+ }
+ #endif
+ }
+ return mem;
+}
+
+static size_t
+default_allocated_size(const void* data)
+{
+ #ifdef NDEBUG
+ (void)data;
+ return 0;
+ #else
+ const struct alloc_counter* counter = data;
+ ASSERT(counter != NULL);
+ return counter->allocated_size;
+ #endif
+}
+
+static size_t
+default_dump
+ (const void* data,
+ char* dump,
+ size_t max_dump_len)
+{
+ #ifdef NDEBUG
+ (void)data;
+ if(dump && max_dump_len)
+ dump[0] = '\0';
+ return 0;
+ #else
+ const struct alloc_counter* counter = data;
+ size_t dump_len = 0;
+ int len = 0;
+
+ ASSERT(counter && (!max_dump_len || dump));
+
+ len = snprintf
+ (dump,
+ max_dump_len,
+ "%zu bytes allocated in %zu allocations.",
+ counter->allocated_size,
+ counter->nb_allocs);
+ ASSERT(len >= 0);
+ dump_len = (size_t)len;
+
+ if((size_t)len >= (max_dump_len - 1)) /* -1 <=> null char. */
+ dump[max_dump_len] = '\0';
+
+ return dump_len;
+ #endif
+}
+
+/*******************************************************************************
+ * Proxy allocator functions
+ ******************************************************************************/
+#define PROXY_DEFAULT_ALIGNMENT 8
+
+struct proxy_data {
+ const char* name;
+ struct mem_allocator* allocator;
+ struct mem_node* node_list;
+};
+
+struct mem_node {
+ struct mem_node* next;
+ struct mem_node* prev;
+ size_t size;
+ const char* filename;
+ unsigned int fileline;
+ char reserved[2];
+};
+
+static void*
+proxy_aligned_alloc
+ (void* data,
+ size_t size,
+ size_t align,
+ const char* filename,
+ unsigned int fileline)
+{
+ struct proxy_data* proxy_data = NULL;
+ char* mem = NULL;
+ size_t node_header_size = 0;
+ size_t node_size = 0;
+ struct mem_node* node = NULL;
+
+ ASSERT(data);
+ proxy_data = data;
+
+ if((IS_POWER_OF_2(align) == 0) || align > 32768)
+ return NULL;
+ align = align < PROXY_DEFAULT_ALIGNMENT ? PROXY_DEFAULT_ALIGNMENT : align;
+
+ node_header_size = ALIGN_SIZE(sizeof(struct mem_node), align);
+ node_size = node_header_size + size;
+ node = MEM_ALIGNED_ALLOC(proxy_data->allocator, node_size, align);
+ if(!node)
+ return NULL;
+
+ mem = (char*)((uintptr_t)node + (uintptr_t)node_header_size);
+ mem[-1] = (char)(align & 0xFF);
+ mem[-2] = (char)((align >> 8) & 0xFF);
+ node->next = proxy_data->node_list;
+ node->prev = NULL;
+ node->filename = filename;
+ node->fileline = fileline;
+ node->size = size;
+ if(proxy_data->node_list)
+ proxy_data->node_list->prev = node;
+ proxy_data->node_list = node;
+ return mem;
+}
+
+static void*
+proxy_alloc
+ (void* data,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ return proxy_aligned_alloc
+ (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
+}
+
+static void*
+proxy_calloc
+ (void* data,
+ size_t nbelmts,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ size_t allocation_size = nbelmts * size;
+ void* mem = proxy_aligned_alloc
+ (data, allocation_size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
+ if(mem)
+ mem = memset(mem, 0, allocation_size);
+ return mem;
+}
+
+static void
+proxy_free(void* data, void* mem)
+{
+ if(mem) {
+ struct proxy_data* proxy_data = NULL;
+ struct mem_node* node = NULL;
+ uintptr_t alignment = 0;
+
+ ASSERT(data);
+ proxy_data = data;
+
+ alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8));
+ node =
+ (void*)((uintptr_t)mem - ALIGN_SIZE(sizeof(struct mem_node), alignment));
+
+ if(node->prev) {
+ node->prev->next = node->next;
+ }
+ if(node->next) {
+ node->next->prev = node->prev;
+ }
+ if(node->prev == NULL) {
+ proxy_data->node_list = node->next;
+ }
+ MEM_FREE(proxy_data->allocator, node);
+ }
+}
+
+static void*
+proxy_realloc
+ (void* data,
+ void* mem,
+ size_t size,
+ const char* filename,
+ unsigned int fileline)
+{
+ if(size == 0) {
+ proxy_free(data, mem);
+ return NULL;
+ } else if(mem == NULL) {
+ return proxy_aligned_alloc
+ (data, size, PROXY_DEFAULT_ALIGNMENT, filename, fileline);
+ } else {
+ struct mem_node* node = NULL;
+ uintptr_t node_header_size = 0;
+ uintptr_t alignment = 0;
+
+ alignment = (uintptr_t)(((char*)mem)[-1] | (((char*)mem)[-2] << 8));
+ node_header_size = ALIGN_SIZE(sizeof(struct mem_node), alignment);
+ node = (void*)((uintptr_t)mem - node_header_size);
+
+ if(node->size == size) {
+ return mem;
+ } else {
+ void* dst = proxy_aligned_alloc
+ (data, size, alignment, filename, fileline);
+ if(!dst) {
+ proxy_free(data, mem);
+ return NULL;
+ } else {
+ dst = memcpy(dst, mem, size < node->size ? size : node->size);
+ proxy_free(data, mem);
+ return dst;
+ }
+ }
+ }
+}
+
+static size_t
+proxy_allocated_size(const void* data)
+{
+ const struct proxy_data* proxy_data = NULL;
+ struct mem_node* node = NULL;
+ size_t allocated_size = 0;
+
+ ASSERT(data);
+ proxy_data = data;
+ for(node = proxy_data->node_list; node != NULL; node = node->next) {
+ allocated_size += malloc_usable_size(node);
+ }
+ return allocated_size;
+}
+
+static size_t
+proxy_dump
+ (const void* data,
+ char* dump,
+ size_t max_dump_len)
+{
+ const struct proxy_data* proxy_data = NULL;
+ struct mem_node* node = NULL;
+ size_t dump_len = 0;
+ size_t avaible_dump_space = max_dump_len ? max_dump_len - 1 /*NULL char*/ : 0;
+
+ ASSERT(data && (!max_dump_len || dump));
+ proxy_data = data;
+
+ for(node = proxy_data->node_list; node != NULL; node = node->next) {
+ if(dump) {
+ const int len = snprintf
+ (dump,
+ avaible_dump_space,
+ "%s: %lu bytes allocated at %s:%u%s",
+ proxy_data->name,
+ (long unsigned)malloc_usable_size(node),
+ node->filename ? node->filename : "none",
+ node->fileline,
+ node->next ? ".\n" : ".");
+ ASSERT(len >= 0);
+ dump_len += (size_t)len;
+
+ if((size_t)len < avaible_dump_space) {
+ dump += len;
+ avaible_dump_space -= (size_t)len;
+ } else if(dump) {
+ dump[avaible_dump_space] = '\0';
+ avaible_dump_space = 0;
+ dump = NULL;
+ }
+ }
+ }
+ return dump_len;
+}
+
+#undef PROXY_DEFAULT_ALIGNMENT
+
+/*******************************************************************************
+ * Default allocator
+ ******************************************************************************/
+static struct alloc_counter default_alloc_counter = {0, 0};
+
+EXPORT_SYM struct mem_allocator mem_default_allocator = {
+ default_alloc,
+ default_calloc,
+ default_realloc,
+ default_aligned_alloc,
+ default_free,
+ default_allocated_size,
+ default_dump,
+ (void*)&default_alloc_counter
+};
+
+/*******************************************************************************
+ * Proxy allocator
+ ******************************************************************************/
+void
+mem_init_proxy_allocator
+ (const char* name,
+ struct mem_allocator* proxy_allocator,
+ struct mem_allocator* allocator)
+{
+ struct proxy_data* proxy_data = NULL;
+
+ if((!allocator) | (!proxy_allocator))
+ goto error;
+
+ proxy_data = MEM_CALLOC(allocator, 1, sizeof(struct proxy_data));
+ if(!proxy_data)
+ goto error;
+ proxy_data->name = name;
+ proxy_data->allocator = allocator;
+ proxy_data->node_list = NULL;
+
+ proxy_allocator->alloc = proxy_alloc;
+ proxy_allocator->calloc = proxy_calloc;
+ proxy_allocator->realloc = proxy_realloc;
+ proxy_allocator->aligned_alloc = proxy_aligned_alloc;
+ proxy_allocator->free = proxy_free;
+ proxy_allocator->allocated_size = proxy_allocated_size;
+ proxy_allocator->dump = proxy_dump;
+ proxy_allocator->data = (void*)proxy_data;
+
+exit:
+ return;
+error:
+ if(proxy_allocator) {
+ ASSERT(proxy_data == NULL);
+ memset(proxy_allocator, 0, sizeof(struct mem_allocator));
+ }
+ goto exit;
+}
+
+void
+mem_shutdown_proxy_allocator(struct mem_allocator* proxy)
+{
+ struct proxy_data* proxy_data = NULL;
+ struct mem_allocator* allocator = NULL;
+
+ ASSERT(proxy);
+ proxy_data = proxy->data;
+ ASSERT(proxy_data->node_list == NULL);
+ allocator = proxy_data->allocator;
+ MEM_FREE(allocator, proxy_data);
+ memset(proxy, 0, sizeof(struct mem_allocator));
+}
+
diff --git a/src/mem_allocator.h b/src/mem_allocator.h
@@ -0,0 +1,112 @@
+#ifndef MEM_ALLOCATOR_H
+#define MEM_ALLOCATOR_H
+
+#include "rsys.h"
+#include <stddef.h>
+
+/*******************************************************************************
+ * Memory allocator interface
+ ******************************************************************************/
+struct mem_allocator {
+ void* (*alloc)
+ (void* data,
+ size_t size,
+ const char* filename,
+ unsigned int fileline);
+
+ void* (*calloc)
+ (void* data,
+ size_t nbelmts,
+ size_t size,
+ const char* filename,
+ unsigned int fileline);
+
+ void* (*realloc)
+ (void* data,
+ void* mem,
+ size_t size,
+ const char* filename,
+ unsigned int fileline);
+
+ void* (*aligned_alloc)
+ (void* data,
+ size_t size,
+ size_t alignment,
+ const char* filename,
+ unsigned int fileline);
+
+ void (*free)
+ (void* data,
+ void* mem);
+
+ size_t (*allocated_size)
+ (const void* data);
+
+ size_t (*dump) /* Return the real dump len (without the null char) */
+ (const void* data,
+ char* dump,
+ size_t max_dump_len); /* Include the null char */
+
+ void* data;
+};
+
+/* Default allocator. */
+extern struct mem_allocator mem_default_allocator;
+
+/*******************************************************************************
+ * Helper macros
+ ******************************************************************************/
+#define MEM_ALLOC(Allocator, Size) \
+ ((Allocator)->alloc((Allocator)->data, (Size), __FILE__, __LINE__))
+
+#define MEM_CALLOC(Allocator, Nb, Size) \
+ ((Allocator)->calloc((Allocator)->data, (Nb), (Size), __FILE__, __LINE__))
+
+#define MEM_REALLOC(Allocator, Mem, Size) \
+ ((Allocator)->realloc((Allocator)->data, (Mem), (Size), __FILE__, __LINE__))
+
+#define MEM_ALIGNED_ALLOC(Allocator, Size, Alignment) \
+ ((Allocator)->aligned_alloc \
+ ((Allocator)->data, (Size), (Alignment), __FILE__, __LINE__))
+
+#define MEM_FREE(Allocator, Mem) \
+ ((Allocator)->free((Allocator)->data, (Mem)))
+
+#define MEM_ALLOCATED_SIZE(Allocator) \
+ ((Allocator)->allocated_size((Allocator)->data))
+
+#define MEM_DUMP(Allocator, Msg, MaxLen) \
+ ((Allocator)->dump((Allocator)->data, (Msg), (MaxLen)))
+
+#define MEM_IS_ALLOCATOR_VALID(Allocator) \
+ ( NULL != (Allocator)->alloc \
+ && NULL != (Allocator)->calloc \
+ && NULL != (Allocator)->realloc \
+ && NULL != (Allocator)->aligned_alloc \
+ && NULL != (Allocator)->free \
+ && NULL != (Allocator)->allocated_size \
+ && NULL != (Allocator)->dump)
+
+/*******************************************************************************
+ * Proxy allocator
+ ******************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+RSYS_API void
+mem_init_proxy_allocator
+ (const char* proxy_name,
+ struct mem_allocator* proxy,
+ struct mem_allocator* allocator);
+
+RSYS_API void
+mem_shutdown_proxy_allocator
+ (struct mem_allocator* proxy_allocator);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* MEM_ALLOCATOR_H */
+
diff --git a/src/mutex.h b/src/mutex.h
@@ -0,0 +1,32 @@
+#ifndef MUTEX_H
+#define MUTEX_H
+
+#include "rsys.h"
+
+struct mutex;
+static FINLINE void mutex_init(struct mutex* mutex);
+static FINLINE void mutex_destroy(struct mutex* mutex);
+static FINLINE void mutex_lock(struct mutex* mutex);
+static FINLINE void mutex_unlock(struct mutex* mutex);
+
+struct mutex_spin;
+static FINLINE void mutex_spin_init(struct mutex_spin* mutex);
+static FINLINE void mutex_spin_destroy(struct mutex_spin* mutex);
+static FINLINE void mutex_spin_lock(struct mutex_spin* mutex);
+static FINLINE void mutex_spin_unlock(struct mutex_spin* mutex);
+
+struct mutex_rw;
+static FINLINE void mutex_rw_init(struct mutex_rw* mutex);
+static FINLINE void mutex_rw_destroy(struct mutex_rw* mutex);
+static FINLINE void mutex_rw_rlock(struct mutex_rw* mutex);
+static FINLINE void mutex_rw_wlock(struct mutex_rw* mutex);
+static FINLINE void mutex_rw_unlock(struct mutex_rw* mutex);
+
+#ifdef RSYS_USE_PTHREADS
+ #include "pthread/mutex.h"
+#else
+ #error "No supported thread library is defined"
+#endif
+
+#endif /* MUTEX_H */
+
diff --git a/src/pthread/condition.h b/src/pthread/condition.h
@@ -0,0 +1,47 @@
+#include <pthread.h>
+
+#ifdef NDEBUG
+ #define PTHREAD__(Func) pthread_##Func
+#else
+ #define PTHREAD__(Func) ASSERT(pthread_##Func == 0)
+#endif
+
+struct cond { pthread_cond_t cond__; };
+
+void
+cond_init(struct cond* cond)
+{
+ ASSERT(cond);
+ PTHREAD__(cond_init(&cond->cond__, NULL));
+}
+
+void
+cond_destroy(struct cond* cond)
+{
+ ASSERT(cond);
+ PTHREAD__(cond_destroy(&cond->cond__));
+}
+
+void
+cond_wait(struct cond* cond, struct mutex* mutex)
+{
+ ASSERT(cond);
+ PTHREAD__(cond_wait(&cond->cond__, &mutex->mutex__));
+}
+
+void
+cond_signal(struct cond* cond)
+{
+ ASSERT(cond);
+ PTHREAD__(cond_signal(&cond->cond__));
+}
+
+void
+cond_broadcast(struct cond* cond)
+{
+ ASSERT(cond);
+ PTHREAD__(cond_broadcast(&cond->cond__));
+}
+
+#undef PTHREAD__
+
diff --git a/src/pthread/mutex.h b/src/pthread/mutex.h
@@ -0,0 +1,116 @@
+#include <pthread.h>
+
+#ifdef NDEBUG
+ #define PTHREAD__(Func) pthread_##Func
+#else
+ #define PTHREAD__(Func) ASSERT(pthread_##Func == 0)
+#endif
+
+/*******************************************************************************
+ * Mutex
+ ******************************************************************************/
+struct mutex { pthread_mutex_t mutex__; };
+
+void
+mutex_init(struct mutex* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(mutex_init(&mutex->mutex__, NULL));
+}
+
+void
+mutex_destroy(struct mutex* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(mutex_destroy(&mutex->mutex__));
+}
+
+void
+mutex_lock(struct mutex* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(mutex_lock(&mutex->mutex__));
+}
+
+void
+mutex_unlock(struct mutex* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(mutex_unlock(&mutex->mutex__));
+}
+
+/*******************************************************************************
+ * Spinlock
+ ******************************************************************************/
+struct mutex_spin { pthread_spinlock_t mutex__; };
+
+void
+mutex_spin_init(struct mutex_spin* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(spin_init(&mutex->mutex__, PTHREAD_PROCESS_PRIVATE));
+}
+
+void
+mutex_spin_destroy(struct mutex_spin* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(spin_destroy(&mutex->mutex__));
+}
+
+void
+mutex_spin_lock(struct mutex_spin* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(spin_lock(&mutex->mutex__));
+}
+
+void
+mutex_spin_unlock(struct mutex_spin* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(spin_unlock(&mutex->mutex__));
+}
+
+/*******************************************************************************
+ * Read Write mutex
+ ******************************************************************************/
+struct mutex_rw { pthread_rwlock_t mutex__; };
+
+static FINLINE void
+mutex_rw_init(struct mutex_rw* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(rwlock_init(&mutex->mutex__, NULL));
+}
+
+static FINLINE void
+mutex_rw_destroy(struct mutex_rw* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(rwlock_destroy(&mutex->mutex__));
+}
+
+static FINLINE void
+mutex_rw_rlock(struct mutex_rw* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(rwlock_rdlock(&mutex->mutex__));
+}
+
+static FINLINE void
+mutex_rw_wlock(struct mutex_rw* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(rwlock_wrlock(&mutex->mutex__));
+}
+
+static FINLINE void
+mutex_rw_unlock(struct mutex_rw* mutex)
+{
+ ASSERT(mutex);
+ PTHREAD__(rwlock_unlock(&mutex->mutex__));
+}
+
+#undef PTHREAD__
+
diff --git a/src/ref_count.h b/src/ref_count.h
@@ -0,0 +1,40 @@
+#ifndef REF_COUNT_H
+#define REF_COUNT_H
+
+#include "atomic.h"
+#include "rsys.h"
+
+typedef atomic_int_T ref_T;
+
+static FINLINE void
+ref_init(ref_T* ref)
+{
+ ASSERT(NULL != ref);
+ *ref = 1;
+}
+
+static FINLINE void
+ref_get(ref_T* ref)
+{
+ ASSERT(NULL != ref);
+ ATOMIC_INCR(ref);
+}
+
+static FINLINE int
+ref_put(ref_T* ref, void (*release)(ref_T*))
+{
+ ASSERT(NULL != ref);
+ ASSERT(NULL != release);
+
+ const int curr = ATOMIC_DECR(ref);
+ ASSERT(curr >= 0);
+
+ if(0 == curr) {
+ release(ref);
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* REF_COUNT_H */
+
diff --git a/src/rsys.h b/src/rsys.h
@@ -0,0 +1,138 @@
+#ifndef RSYS_H
+#define RSYS_H
+
+#ifndef __GNUC__
+ #error "Unsupported compiler"
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*******************************************************************************
+ * Platform
+ ******************************************************************************/
+#if defined(__unix__) || defined(__unix) || defined(unix)
+ #define PLATFORM_UNIX
+#else
+ #error "Unsupported platform"
+#endif
+
+/*******************************************************************************
+ * Compiler
+ ******************************************************************************/
+#if defined( __GNUC__ )
+ #define COMPILER_GCC
+#else
+ #error "Unsupported compiler"
+#endif
+
+/*******************************************************************************
+ * Symbol visibility
+ ******************************************************************************/
+#define EXPORT_SYM __attribute__((visibility("default")))
+#define IMPORT_SYM
+#define LOCAL_SYM __attribute__((visibility("hidden")))
+
+#if defined(RSYS_SHARED_BUILD)
+ #define RSYS_API extern EXPORT_SYM
+#else
+ #define RSYS_API extern IMPORT_SYM
+#endif
+
+/*******************************************************************************
+ * Code inlining
+ ******************************************************************************/
+#define FINLINE inline __attribute__((always_inline))
+#define INLINE inline
+#define NOINLINE __attribute__((noinline))
+
+/*******************************************************************************
+ * Data alignment
+ ******************************************************************************/
+#define ALIGN(Size) __attribute__((aligned(Size)))
+#define ALIGNOF(Type) __alignof__(Type)
+#define ALIGN_SIZE(Size, Algnt) (((Size) + ((Algnt) - 1)) & ~((Algnt) - 1))
+#define IS_ALIGNED(Addr, Algnt) (((uintptr_t)(Addr) & ((Algnt)-1)) == 0)
+
+/*******************************************************************************
+ * Code checking
+ ******************************************************************************/
+#ifdef NDEBUG
+ #define ASSERT(C) (void)0
+#else
+ #include <assert.h>
+ #define ASSERT(C) assert(C)
+#endif
+
+#define STATIC_ASSERT(Cond, Msg) char STATIC_ASSERT_##Msg[1 - 2*(!(Cond))]
+
+#define FATAL(Msg) \
+ { \
+ fprintf(stderr, Msg); \
+ exit(-1); \
+ } (void)0
+
+#define CHECK(A, B) \
+ { \
+ if((A) != (B)) \
+ FATAL("error:" STR( __FILE__ )":"STR( __LINE__ )"\n"); \
+ } (void)0
+
+#define NCHECK(A, B) \
+ { \
+ if((A) == (B)) \
+ FATAL("error:" STR( __FILE__ )":"STR( __LINE__ )"\n"); \
+ } (void)0
+
+/*******************************************************************************
+ * Branch prediction information
+ ******************************************************************************/
+#define LIKELY(X) __builtin_expect((X), 1)
+#define UNLIKELY(X) __builtin_expect((X), 0)
+
+/*******************************************************************************
+ * SIMD instruction sets
+ ******************************************************************************/
+#ifdef __SSE__
+ #define SIMD_SSE
+#endif
+
+#ifdef __SSE2__
+ #define SIMD_SSE2
+#endif
+
+#ifdef __SSE3__
+ #define SIMD_SSE3
+#endif
+
+#ifdef __SSSE3__
+ #define SIMD_SSSE3
+#endif
+
+/*******************************************************************************
+ * Miscellaneous
+ ******************************************************************************/
+#define BIT(Num) (1 << (Num))
+#define CONCAT__(A, B) A ## B
+#define CONCAT(A, B) CONCAT__(A, B)
+
+#define CONTAINER_OF(Ptr, Type, Member) \
+ ((Type*)((uintptr_t)Ptr - offsetof(Type, Member)))
+
+#define COUNTER __COUNTER__
+#define FOR_EACH(Type, Id, Start, End) \
+ for(Type (Var) = (Start); (Var) < (End); ++(Var))
+
+#define IS_MEMORY_OVERLAPPED(D0, Sz0, D1, Sz1) \
+ (((intptr_t)(D0) >= (intptr_t)(D1) && \
+ (intptr_t)(D0) < ((intptr_t)(D1) + (intptr_t)(Sz1))) || \
+ (((intptr_t)(D0) + (intptr_t)(Sz0)) >= (intptr_t)(D1) && \
+ ((intptr_t)(D0) + (intptr_t)(Sz0)) < ((intptr_t)(D1) + (intptr_t)(Sz1))))
+
+#define STR__(X) #X
+#define STR(X) STR__(X)
+
+#endif /* SNLSYS_H */
+
diff --git a/src/signal.h b/src/signal.h
@@ -0,0 +1,65 @@
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+#include "list.h"
+#include "snlsys.h"
+
+/*******************************************************************************
+ *
+ * Signal declaration and functions
+ *
+ ******************************************************************************/
+#define SIGNALS_LIST(Slst, ClbkType, Count) \
+ struct { \
+ ClbkType callbacks_list[(Count)]; \
+ } Slst
+
+#define SIGNALS_LIST_INIT(Slst) \
+ { \
+ unsigned i = 0; \
+ for(i = 0; \
+ i < sizeof((Slst)->callbacks_list) / sizeof((Slst)->callbacks_list[0]);\
+ ++i ) { \
+ list_init(&(Slst)->callbacks_list[i].node); \
+ } \
+ } (void)0
+
+#define SIGNAL_CONNECT_CALLBACK(Slst, Signal, Clbk) \
+ list_add(&(Slst)->callbacks_list[(Signal)].node, &(Clbk)->node)
+
+#define SIGNAL_INVOKE(Slst, Signal, ...) \
+ { \
+ struct list_node* pos = NULL; \
+ typedef TYPEOF((Slst)->callbacks_list[0]) ClbkType; \
+ LIST_FOR_EACH(pos, &(Slst)->callbacks_list[(Signal)].node) { \
+ ClbkType* clbk = CONTAINER_OF(pos, ClbkType, node); \
+ clbk->func(__VA_ARGS__, clbk->data); \
+ } \
+ } (void)0
+
+/*******************************************************************************
+ *
+ * Callback data structure that may be connected to a signal
+ *
+ ******************************************************************************/
+#define CALLBACK(Name, ...) \
+ typedef struct { \
+ struct list_node node; \
+ void (*func)(__VA_ARGS__, void* data); \
+ void* data; \
+ } Name
+
+#define CALLBACK_INIT(Clbk) \
+ list_init(&(Clbk)->node)
+
+#define CALLBACK_SETUP(Clbk, Func, Data) \
+ { \
+ (Clbk)->func = Func; \
+ (Clbk)->data = Data; \
+ } (void)0
+
+#define CALLBACK_DISCONNECT(Clbk) \
+ list_del(&(Clbk)->node)
+
+#endif /* CALLBACK_H */
+
diff --git a/src/test_atomic.c b/src/test_atomic.c
@@ -0,0 +1,51 @@
+#include "atomic.h"
+#include "rsys.h"
+
+int
+main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+
+ atomic_int_T atom = 0;
+ int tmp;
+
+ tmp = ATOMIC_INCR(&atom);
+ CHECK(atom, 1);
+ CHECK(tmp, 1);
+ tmp = ATOMIC_ADD(&atom, 5);
+ CHECK(atom, 6);
+ CHECK(tmp, 6);
+ tmp = ATOMIC_DECR(&atom);
+ CHECK(atom, 5);
+ CHECK(tmp, 5);
+ tmp = ATOMIC_SUB(&atom, 7);
+ CHECK(atom, -2);
+ CHECK(tmp, -2);
+
+ atom = 0;
+ tmp = ATOMIC_FETCH_AND_INCR(&atom);
+ CHECK(atom, 1);
+ CHECK(tmp, 0);
+ tmp = ATOMIC_FETCH_AND_ADD(&atom, 5);
+ CHECK(atom, 6);
+ CHECK(tmp, 1);
+ tmp = ATOMIC_FETCH_AND_DECR(&atom);
+ CHECK(atom, 5);
+ CHECK(tmp, 6);
+ tmp = ATOMIC_FETCH_AND_SUB(&atom, 7);
+ CHECK(atom, -2);
+ CHECK(tmp, 5);
+
+ tmp = ATOMIC_CMP_AND_SWAP(&atom, 0, -1);
+ CHECK(atom, -2);
+ CHECK(tmp, -2);
+ tmp = ATOMIC_CMP_AND_SWAP(&atom, 0, -2);
+ CHECK(atom, 0);
+ CHECK(tmp, -2);
+ ATOMIC_FETCH_AND_STORE(&atom, 9, &tmp);
+ CHECK(atom, 9);
+ CHECK(tmp, 0);
+
+ return 0;
+}
+
diff --git a/src/test_condition.c b/src/test_condition.c
@@ -0,0 +1,9 @@
+#include "condition.h"
+#include <omp.h>
+
+int
+main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+ return 0;
+}
diff --git a/src/test_list.c b/src/test_list.c
@@ -0,0 +1,193 @@
+#include "list.h"
+#include "mem_allocator.h"
+#include "rsys.h"
+
+int
+main(int argc, char** argv)
+{
+ struct elmt {
+ struct list_node node;
+ char c;
+ } elmt0, elmt1, elmt2;
+ struct list_node list, list1;
+ int i = 0;
+
+ (void)argc;
+ (void)argv;
+
+ list_init(&list);
+ list_init(&list1);
+ list_init(&elmt0.node);
+ list_init(&elmt1.node);
+ list_init(&elmt2.node);
+
+ CHECK(is_list_empty(&list), 1);
+
+ elmt0.c = 'a';
+ list_add(&list, &elmt0.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(list_head(&list), &elmt0.node);
+
+ elmt1.c = 'b';
+ list_add(&list, &elmt1.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(elmt1.node.next, &elmt0.node);
+ CHECK(elmt1.node.prev, &list);
+ CHECK(elmt1.node.next->prev, &elmt1.node);
+ CHECK(list_head(&list), &elmt1.node);
+
+ elmt2.c = 'c';
+ list_add_tail(&list, &elmt2.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(elmt2.node.next, &list);
+ CHECK(elmt2.node.prev, &elmt0.node);
+ CHECK(elmt2.node.prev->prev, &elmt1.node);
+ CHECK(elmt1.node.next->next, &elmt2.node);
+ CHECK(elmt0.node.next, &elmt2.node);
+ CHECK(list_head(&list), &elmt1.node);
+ CHECK(list_tail(&list), &elmt2.node);
+
+ list_del(&elmt0.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(elmt2.node.next, &list);
+ CHECK(elmt2.node.prev, &elmt1.node);
+ CHECK(elmt1.node.next, &elmt2.node);
+ CHECK(elmt1.node.prev, &list);
+ CHECK(list_head(&list), &elmt1.node);
+ CHECK(list_tail(&list), &elmt2.node);
+
+ list_del(&elmt2.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(elmt1.node.next, &list);
+ CHECK(elmt1.node.prev, &list);
+ CHECK(list_head(&list), &elmt1.node);
+ CHECK(list_tail(&list), &elmt1.node);
+
+ list_del(&elmt1.node);
+ CHECK(is_list_empty(&list), 1);
+
+ list_add(&list, &elmt2.node);
+ list_add(&list, &elmt1.node);
+ list_add(&list, &elmt0.node);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(elmt2.node.next, &list);
+ CHECK(elmt2.node.prev, &elmt1.node);
+ CHECK(elmt1.node.next, &elmt2.node);
+ CHECK(elmt1.node.prev, &elmt0.node);
+ CHECK(elmt0.node.next, &elmt1.node);
+ CHECK(elmt0.node.prev, &list);
+ CHECK(list_head(&list), &elmt0.node);
+ CHECK(list_tail(&list), &elmt2.node);
+
+ CHECK(is_list_empty(&list1), 1);
+ list_move(&elmt1.node, &list1);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(is_list_empty(&list1), 0);
+ CHECK(elmt2.node.next, &list);
+ CHECK(elmt2.node.prev, &elmt0.node);
+ CHECK(elmt1.node.next, &list1);
+ CHECK(elmt1.node.prev, &list1);
+ CHECK(elmt0.node.next, &elmt2.node);
+ CHECK(elmt0.node.prev, &list);
+ CHECK(list_head(&list), &elmt0.node);
+ CHECK(list_tail(&list), &elmt2.node);
+ CHECK(list_head(&list1), &elmt1.node);
+ CHECK(list_tail(&list1), &elmt1.node);
+
+ list_move_tail(&elmt2.node, &list1);
+ CHECK(is_list_empty(&list), 0);
+ CHECK(is_list_empty(&list1), 0);
+ CHECK(elmt2.node.next, &list1);
+ CHECK(elmt2.node.prev, &elmt1.node);
+ CHECK(elmt1.node.next, &elmt2.node);
+ CHECK(elmt1.node.prev, &list1);
+ CHECK(elmt0.node.next, &list);
+ CHECK(elmt0.node.prev, &list);
+ CHECK(list_head(&list), &elmt0.node);
+ CHECK(list_tail(&list), &elmt0.node);
+ CHECK(list_head(&list1), &elmt1.node);
+ CHECK(list_tail(&list1), &elmt2.node);
+
+ list_move(&elmt0.node, &list1);
+ CHECK(is_list_empty(&list), 1);
+ CHECK(is_list_empty(&list1), 0);
+ CHECK(elmt2.node.next, &list1);
+ CHECK(elmt2.node.prev, &elmt1.node);
+ CHECK(elmt1.node.next, &elmt2.node);
+ CHECK(elmt1.node.prev, &elmt0.node);
+ CHECK(elmt0.node.next, &elmt1.node);
+ CHECK(elmt0.node.prev, &list1);
+ CHECK(list_head(&list1), &elmt0.node);
+ CHECK(list_tail(&list1), &elmt2.node);
+
+ i = 0;
+ LIST_FOR_EACH(n, &list1) {
+ struct elmt* e = CONTAINER_OF(n, struct elmt, node);
+ CHECK(e->c, 'a' + i);
+ ++i;
+ }
+ CHECK(i, 3);
+
+ i = 3;
+ LIST_FOR_EACH_REVERSE(n, &list1) {
+ struct elmt* e = CONTAINER_OF(n, struct elmt, node);
+ --i;
+ CHECK(e->c, 'a' + i);
+ }
+ CHECK(i, 0);
+
+ i = 0;
+ LIST_FOR_EACH_SAFE(n, &list1) {
+ list_move_tail(n, &list);
+ struct elmt* e = CONTAINER_OF(n, struct elmt, node);
+ CHECK(e->c, 'a' + i);
+ ++i;
+ }
+ CHECK(i, 3);
+ CHECK(is_list_empty(&list1), 1);
+ CHECK(is_list_empty(&list), 0);
+
+ i = 3;
+ LIST_FOR_EACH_REVERSE_SAFE(n, &list) {
+ list_move(n, &list1);
+ struct elmt* e = CONTAINER_OF(n, struct elmt, node);
+ --i;
+ CHECK(e->c, 'a' + i);
+ }
+ CHECK(i, 0);
+ CHECK(is_list_empty(&list1), 0);
+ CHECK(is_list_empty(&list), 1);
+
+ i = 0;
+ LIST_FOR_EACH(n, &list1) {
+ struct elmt* e = CONTAINER_OF(n, struct elmt, node);
+ CHECK(e->c, 'a' + i);
+ ++i;
+ }
+ CHECK(i, 3);
+
+ list_move(&elmt1.node, &list1);
+ CHECK(elmt2.node.next, &list1);
+ CHECK(elmt2.node.prev, &elmt0.node);
+ CHECK(elmt1.node.next, &elmt0.node);
+ CHECK(elmt1.node.prev, &list1);
+ CHECK(elmt0.node.next, &elmt2.node);
+ CHECK(elmt0.node.prev, &elmt1.node);
+ CHECK(list_head(&list1), &elmt1.node);
+ CHECK(list_tail(&list1), &elmt2.node);
+
+ list_move_tail(&elmt0.node, &list1);
+ CHECK(elmt2.node.next, &elmt0.node);
+ CHECK(elmt2.node.prev, &elmt1.node);
+ CHECK(elmt1.node.next, &elmt2.node);
+ CHECK(elmt1.node.prev, &list1);
+ CHECK(elmt0.node.next, &list1);
+ CHECK(elmt0.node.prev, &elmt2.node);
+ CHECK(list_head(&list1), &elmt1.node);
+ CHECK(list_tail(&list1), &elmt0.node);
+
+ CHECK(MEM_ALLOCATED_SIZE(&mem_default_allocator), 0);
+
+ return 0;
+}
+
diff --git a/src/test_mem_allocator.c b/src/test_mem_allocator.c
@@ -0,0 +1,90 @@
+#include "mem_allocator.h"
+#include "rsys.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+regular_test(struct mem_allocator* allocator)
+{
+ char dump[BUFSIZ];
+ void* p = NULL;
+ void* q[3] = {NULL, NULL, NULL};
+ size_t i = 0;
+
+ p = MEM_ALIGNED_ALLOC(allocator, 1024, ALIGNOF(char));
+ NCHECK(p, NULL);
+ CHECK(IS_ALIGNED((uintptr_t)p, ALIGNOF(char)), 1);
+ MEM_FREE(allocator, p);
+
+ q[0] = MEM_ALIGNED_ALLOC(allocator, 10, 8);
+ q[1] = MEM_CALLOC(allocator, 1, 58);
+ q[2] = MEM_ALLOC(allocator, 78);
+ NCHECK(q[0], NULL);
+ NCHECK(q[1], NULL);
+ NCHECK(q[2], NULL);
+ CHECK(IS_ALIGNED((uintptr_t)q[0], 8), 1);
+
+ p = MEM_CALLOC(allocator, 2, 2);
+ NCHECK(p, NULL);
+ for(i = 0; i < 4; ++i)
+ CHECK(((char*)p)[i], 0);
+ for(i = 0; i < 4; ++i)
+ ((char*)p)[i] = (char)i;
+
+ MEM_DUMP(allocator, dump, BUFSIZ);
+ printf("dump:\n%s\n", dump);
+ MEM_DUMP(allocator, dump, 16);
+ printf("truncated dump:\n%s\n", dump);
+ MEM_DUMP(allocator, NULL, 0); /* may not crash */
+
+ MEM_FREE(allocator, q[1]);
+
+ p = MEM_REALLOC(allocator, p, 8);
+ for(i = 0; i < 4; ++i)
+ CHECK(((char*)p)[i], (char)i);
+ for(i = 4; i < 8; ++i)
+ ((char*)p)[i] = (char)i;
+
+ MEM_FREE(allocator, q[2]);
+
+ p = MEM_REALLOC(allocator, p, 5);
+ for(i = 0; i < 5; ++i)
+ CHECK(((char*)p)[i], (char)i);
+
+ MEM_FREE(allocator, p);
+
+ p = NULL;
+ p = MEM_REALLOC(allocator, NULL, 16);
+ NCHECK(p, NULL);
+ p = MEM_REALLOC(allocator, p, 0);
+
+ MEM_FREE(allocator, q[0]);
+
+ CHECK(MEM_ALIGNED_ALLOC(allocator, 1024, 0), NULL);
+ CHECK(MEM_ALIGNED_ALLOC(allocator, 1024, 3), NULL);
+ CHECK(MEM_ALLOCATED_SIZE(allocator), 0);
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+
+ (void)argc;
+ (void)argv;
+
+ printf("Default allocator:\n");
+ regular_test(&mem_default_allocator);
+
+ printf("\nProxy allocator\n");
+ mem_init_proxy_allocator("utest", &allocator, &mem_default_allocator);
+ regular_test(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+
+ CHECK(MEM_ALLOCATED_SIZE(&mem_default_allocator), 0);
+
+ return 0;
+}
+
diff --git a/src/test_mutex.c b/src/test_mutex.c
@@ -0,0 +1,215 @@
+#include "clock_time.h"
+#include "mutex.h"
+
+#include <string.h>
+#include <omp.h>
+
+static const char src_str[] =
+"Rcvfbqr 1, XARR-QRRC VA GUR QRNQ:\n\
+---------------------------------\n\
+\n\
+BAPR LBH ORNG GUR OVT ONQNFFRF NAQ PYRNA BHG GUR ZBBA ONFR LBH'ER FHCCBFRQ GB\n\
+JVA, NERA'G LBH? NERA'G LBH? JURER'F LBHE SNG ERJNEQ NAQ GVPXRG UBZR? JUNG\n\
+GUR URYY VF GUVF? VG'F ABG FHCCBFRQ GB RAQ GUVF JNL!\n\
+\n\
+VG FGVAXF YVXR EBGGRA ZRNG, OHG YBBXF YVXR GUR YBFG QRVZBF ONFR. YBBXF YVXR\n\
+LBH'ER FGHPX BA GUR FUBERF BS URYY. GUR BAYL JNL BHG VF GUEBHTU.\n\
+\n\
+GB PBAGVAHR GUR QBBZ RKCREVRAPR, CYNL GUR FUBERF BS URYY NAQ VGF NZNMVAT\n\
+FRDHRY, VASREAB!\n\
+\n\
+Rcvfbqr 2, GUR FUBERF BS URYY:\n\
+------------------------------\n\
+\n\
+LBH'IR QBAR VG! GUR UVQRBHF PLORE- QRZBA YBEQ GUNG EHYRQ GUR YBFG QRVZBF ZBBA\n\
+ONFR UNF ORRA FYNVA NAQ LBH NER GEVHZCUNAG! OHG ... JURER NER LBH? LBH\n\
+PYNZORE GB GUR RQTR BS GUR ZBBA NAQ YBBX QBJA GB FRR GUR NJSHY GEHGU.\n\
+\n\
+QRVZBF SYBNGF NOBIR URYY VGFRYS! LBH'IR ARIRE URNEQ BS NALBAR RFPNCVAT SEBZ\n\
+URYY, OHG LBH'YY ZNXR GUR ONFGNEQF FBEEL GURL RIRE URNEQ BS LBH! DHVPXYL, LBH\n\
+ENCCRY QBJA GB GUR FHESNPR BS URYY.\n\
+\n\
+ABJ, VG'F BA GB GUR SVANY PUNCGRE BS QBBZ! -- VASREAB.\n\
+\n\
+Rcvfbqr 3, VASREAB:\n\
+-------------------\n\
+\n\
+GUR YBNGUFBZR FCVQREQRZBA GUNG ZNFGREZVAQRQ GUR VAINFVBA BS GUR ZBBA ONFRF\n\
+NAQ PNHFRQ FB ZHPU QRNGU UNF UNQ VGF NFF XVPXRQ SBE NYY GVZR.\n\
+\n\
+N UVQQRA QBBEJNL BCRAF NAQ LBH RAGRE. LBH'IR CEBIRA GBB GBHTU SBE URYY GB\n\
+PBAGNVA, NAQ ABJ URYY NG YNFG CYNLF SNVE -- SBE LBH RZRETR SEBZ GUR QBBE GB\n\
+FRR GUR TERRA SVRYQF BS RNEGU! UBZR NG YNFG.\n\
+\n\
+LBH JBAQRE JUNG'F ORRA UNCCRAVAT BA RNEGU JUVYR LBH JRER ONGGYVAT RIVY\n\
+HAYRNFURQ. VG'F TBBQ GUNG AB URYY- FCNJA PBHYQ UNIR PBZR GUEBHTU GUNG QBBE\n\
+JVGU LBH ...\n\
+\n\
+Rcvfbqr 4, GUL SYRFU PBAFHZRQ:\n\
+------------------------------\n\
+\n\
+GUR FCVQRE ZNFGREZVAQ ZHFG UNIR FRAG SBEGU VGF YRTVBAF BS URYYFCNJA ORSBER\n\
+LBHE SVANY PBASEBAGNGVBA JVGU GUNG GREEVOYR ORNFG SEBZ URYY. OHG LBH FGRCCRQ\n\
+SBEJNEQ NAQ OEBHTUG SBEGU RGREANY QNZANGVBA NAQ FHSSREVAT HCBA GUR UBEQR NF N\n\
+GEHR UREB JBHYQ VA GUR SNPR BS FBZRGUVAT FB RIVY.\n\
+\n\
+ORFVQRF, FBZRBAR JNF TBAAN CNL SBE JUNG UNCCRARQ GB QNVFL, LBHE CRG ENOOVG.\n\
+\n\
+OHG ABJ, LBH FRR FCERNQ ORSBER LBH ZBER CBGRAGVNY CNVA NAQ TVOOVGHQR NF N\n\
+ANGVBA BS QRZBAF EHA NZBX VA BHE PVGVRF.\n\
+\n\
+ARKG FGBC, URYY BA RNEGU!";
+
+enum mutex_type {
+ MUTEX_COMMON,
+ MUTEX_SPIN,
+ MUTEX_RW
+};
+
+struct string
+{
+ struct mutex mutex;
+ struct mutex_spin mutex_spin;
+ struct mutex_rw mutex_rw;
+ char str[sizeof(src_str)/sizeof(char) + 1 /* +1 <=< '\0'*/ ];
+ int i;
+};
+
+static void
+string_write(struct string* string, const enum mutex_type type)
+{
+ ASSERT(string);
+
+ switch(type) {
+ case MUTEX_COMMON:
+ {
+ for(;;) {
+ mutex_lock(&string->mutex);
+ if((unsigned)string->i >= sizeof(src_str)/sizeof(char) + 1) {
+ mutex_unlock(&string->mutex);
+ break;
+ }
+
+ string->str[string->i] = src_str[string->i];
+ ++string->i;
+
+ mutex_unlock(&string->mutex);
+ }
+ }
+ break;
+ case MUTEX_SPIN:
+ {
+ for(;;) {
+ mutex_spin_lock(&string->mutex_spin);
+ if((unsigned)string->i >= sizeof(src_str)/sizeof(char) + 1) {
+ mutex_spin_unlock(&string->mutex_spin);
+ break;
+ }
+
+ string->str[string->i] = src_str[string->i];
+ ++string->i;
+
+ mutex_spin_unlock(&string->mutex_spin);
+ }
+ }
+ break;
+ case MUTEX_RW:
+ {
+ for(;;) {
+ mutex_rw_wlock(&string->mutex_rw);
+ if((unsigned)string->i >= sizeof(src_str)/sizeof(char) + 1) {
+ mutex_rw_unlock(&string->mutex_rw);
+ break;
+ }
+
+ string->str[string->i] = src_str[string->i];
+ ++string->i;
+
+ mutex_rw_unlock(&string->mutex_rw);
+ }
+ }
+ break;
+ default: ASSERT(0); break;
+ }
+}
+
+static void
+string_read(struct string* string)
+{
+ ASSERT(string);
+ int i = 0;
+ do {
+ mutex_rw_rlock(&string->mutex_rw);
+ i = string->i;
+
+ mutex_rw_unlock(&string->mutex_rw);
+
+ } while( (unsigned)i < sizeof(src_str)/sizeof(char));
+
+ mutex_rw_rlock(&string->mutex_rw);
+ printf("%s\n", string->str);
+ mutex_rw_unlock(&string->mutex_rw);
+}
+
+static void
+test_mutex(const enum mutex_type type)
+{
+ struct string string = { .str = { [0] = '\0' }, .i = 0 };
+ switch(type) {
+ case MUTEX_COMMON: mutex_init(&string.mutex); break;
+ case MUTEX_SPIN: mutex_spin_init(&string.mutex_spin); break;
+ case MUTEX_RW: mutex_rw_init(&string.mutex_rw); break;
+ default: ASSERT(0); break;
+ }
+
+ time_T time_start, time_end, time_res;
+ time_current(&time_start);
+ #pragma omp parallel sections
+ {
+ #pragma omp section
+ if(type == MUTEX_RW) {
+ string_read(&string);
+ }
+
+ #pragma omp section
+ string_write(&string, type);
+ #pragma omp section
+ string_write(&string, type);
+ }
+ time_current(&time_end);
+ time_sub(&time_res, &time_end, &time_start);
+
+ char dump[32];
+ time_dump
+ (&time_res,
+ TIME_MSEC|TIME_USEC,
+ NULL,
+ dump,
+ sizeof(dump)/sizeof(char));
+ printf("%s\n", dump);
+
+ CHECK(string.i, sizeof(src_str)/sizeof(char) + 1);
+ CHECK(strcmp(string.str, src_str), 0);
+
+ if(type == MUTEX_RW) {
+ #pragma omp taskwait
+ }
+
+ switch(type) {
+ case MUTEX_COMMON: mutex_destroy(&string.mutex); break;
+ case MUTEX_SPIN: mutex_spin_destroy(&string.mutex_spin); break;
+ case MUTEX_RW: mutex_rw_destroy(&string.mutex_rw); break;
+ default: ASSERT(0); break;
+ }
+}
+
+int
+main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+ test_mutex(MUTEX_COMMON);
+ test_mutex(MUTEX_SPIN);
+ test_mutex(MUTEX_RW);
+ return 0;
+}
+
diff --git a/src/test_ref.c b/src/test_ref.c
@@ -0,0 +1,33 @@
+#include "ref_count.h"
+
+struct test {
+ ref_T ref;
+ int val;
+};
+
+static void
+release(ref_T* ref)
+{
+ ASSERT(NULL != ref);
+ CHECK(CONTAINER_OF(ref, struct test, ref)->val, (int)0xDEADBEEF);
+}
+
+int
+main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+
+ struct test test;
+ ref_init(&test.ref);
+ test.val = (int)0xDEADBEEF;
+
+ ref_get(&test.ref);
+ ref_get(&test.ref);
+ ref_get(&test.ref);
+
+ CHECK(ref_put(&test.ref, release), 0);
+ CHECK(ref_put(&test.ref, release), 0);
+ CHECK(ref_put(&test.ref, release), 0);
+ CHECK(ref_put(&test.ref, release), 1);
+ return 0;
+}
diff --git a/src/test_signal.c b/src/test_signal.c
@@ -0,0 +1,117 @@
+#include "mem_allocator.h"
+#include "signal.h"
+
+struct ctxt {
+ int sig0_func1_invoked;
+ int sig0_func2_sum;
+ int sig1_func_sum;
+};
+
+CALLBACK(clbk_T, struct ctxt*);
+
+enum test_signal {
+ SIG0,
+ SIG1,
+ SIGNALS_COUNT
+};
+
+static void
+sig0_func1(struct ctxt* ctxt, void* data)
+{
+ CHECK(data, NULL);
+ ctxt->sig0_func1_invoked = 1;
+}
+
+static void
+sig0_func2(struct ctxt* ctxt, void* data)
+{
+ NCHECK(data, NULL);
+ ctxt->sig0_func2_sum += *((int*)data);
+}
+
+static void
+sig1_func(struct ctxt* ctxt, void* data)
+{
+ NCHECK(data, NULL);
+ ctxt->sig1_func_sum += *(int*)data;
+}
+
+int
+main(int argc, char** argv)
+{
+ (void)argc;
+ (void)argv;
+ struct ctxt ctxt;
+
+ SIGNALS_LIST(slst, clbk_T, SIGNALS_COUNT);
+ SIGNALS_LIST_INIT(&slst);
+
+ clbk_T clbk0_a;
+ clbk_T clbk0_b;
+ clbk_T clbk0_c;
+ clbk_T clbk1_a;
+ clbk_T clbk1_b;
+ CALLBACK_INIT(&clbk0_a);
+ CALLBACK_INIT(&clbk0_b);
+ CALLBACK_INIT(&clbk0_c);
+ CALLBACK_INIT(&clbk1_a);
+ CALLBACK_INIT(&clbk1_b);
+ CALLBACK_SETUP(&clbk0_a, sig0_func1, NULL);
+ CALLBACK_SETUP(&clbk0_b, sig0_func2, (int[]){12});
+ CALLBACK_SETUP(&clbk0_c, sig0_func2, (int[]){-1});
+ CALLBACK_SETUP(&clbk1_a, sig1_func, (int[]){2});
+ CALLBACK_SETUP(&clbk1_b, sig1_func, (int[]){1});
+
+ ctxt.sig0_func1_invoked = 0;
+ ctxt.sig0_func2_sum = 0;
+ ctxt.sig1_func_sum = 0;
+
+ SIGNAL_INVOKE(&slst, SIG0, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 0);
+ CHECK(ctxt.sig0_func2_sum, 0);
+ CHECK(ctxt.sig1_func_sum, 0);
+
+ SIGNAL_INVOKE(&slst, SIG1, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 0);
+ CHECK(ctxt.sig0_func2_sum, 0);
+ CHECK(ctxt.sig1_func_sum, 0);
+
+ SIGNAL_CONNECT_CALLBACK(&slst, SIG0, &clbk0_a);
+ SIGNAL_CONNECT_CALLBACK(&slst, SIG0, &clbk0_b);
+ SIGNAL_CONNECT_CALLBACK(&slst, SIG0, &clbk0_c);
+ SIGNAL_INVOKE(&slst, SIG0, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 1);
+ CHECK(ctxt.sig0_func2_sum, 11);
+ CHECK(ctxt.sig1_func_sum, 0);
+
+ CALLBACK_DISCONNECT(&clbk0_c);
+ ctxt.sig0_func1_invoked = 0;
+ ctxt.sig0_func2_sum = 0;
+ ctxt.sig1_func_sum = 0;
+ SIGNAL_INVOKE(&slst, SIG0, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 1);
+ CHECK(ctxt.sig0_func2_sum, 12);
+ CHECK(ctxt.sig1_func_sum, 0);
+
+ SIGNAL_CONNECT_CALLBACK(&slst, SIG1, &clbk1_a);
+ SIGNAL_INVOKE(&slst, SIG0, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 1);
+ CHECK(ctxt.sig0_func2_sum, 24);
+ CHECK(ctxt.sig1_func_sum, 0);
+
+ SIGNAL_INVOKE(&slst, SIG1, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 1);
+ CHECK(ctxt.sig0_func2_sum, 24);
+ CHECK(ctxt.sig1_func_sum, 2);
+
+ SIGNAL_CONNECT_CALLBACK(&slst, SIG1, &clbk1_b);
+ SIGNAL_INVOKE(&slst, SIG1, &ctxt);
+ CHECK(ctxt.sig0_func1_invoked, 1);
+ CHECK(ctxt.sig0_func2_sum, 24);
+ CHECK(ctxt.sig1_func_sum, 5);
+
+ CHECK(MEM_ALLOCATED_SIZE(&mem_default_allocator), 0);
+
+ return 0;
+}
+