rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit d1bbf57555b5b43252252e584dd3db3ab792658c
Author: vaplv <vaplv@free.fr>
Date:   Wed, 18 Sep 2013 10:23:18 +0200

First commit

Diffstat:
ALICENSE | 24++++++++++++++++++++++++
AREADME.md | 4++++
Asrc/CMakeLists.txt | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/atomic.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/clock_time.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/clock_time.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/condition.h | 22++++++++++++++++++++++
Asrc/image.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/image.h | 23+++++++++++++++++++++++
Asrc/list.h | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/math.h | 32++++++++++++++++++++++++++++++++
Asrc/mem_allocator.c | 481+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mem_allocator.h | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mutex.h | 32++++++++++++++++++++++++++++++++
Asrc/pthread/condition.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pthread/mutex.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ref_count.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/rsys.h | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/signal.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_atomic.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_condition.c | 9+++++++++
Asrc/test_list.c | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_mem_allocator.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_mutex.c | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_ref.c | 33+++++++++++++++++++++++++++++++++
Asrc/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; +} +