From 8c70a1f96c6c5d280d397e47d520a534e6ae36f6 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Wed, 7 May 2025 22:01:38 +0200 Subject: [PATCH] Remove obsolete m-concurrent.h header --- README.md | 158 ------- example/ex11-small-name.c | 1 - m-concurrent.h | 911 -------------------------------------- tests/test-mconcurrent.c | 250 ----------- 4 files changed, 1320 deletions(-) delete mode 100644 m-concurrent.h delete mode 100644 tests/test-mconcurrent.c diff --git a/README.md b/README.md index 73ef5970..dc1f43d4 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,6 @@ Finally, headers for compatibility with non C11 compilers: * [m-thread.h](#m-thread): header for providing a very thin layer across multiple implementation of mutex/threads (C11/PTHREAD/WIN32). The following headers are obsolete and will be removed in the next major release: -* [m-concurrent.h](#m-concurrent): header for transforming a container into a concurrent container (thread safe), * [m-mempool.h](#m-mempool): header for creating specialized & fast memory allocator, Each containers define their iterators (if it is meaningful). @@ -5276,163 +5275,6 @@ Afterwards, `list2` is emptied. _________________ -### M-CONCURRENT - -> [!NOTE] -> This header is obsolete: M-SHARED-PTR should be used instead. - -This header is for transforming a standard container (`LIST`, `ARRAY`, `DICT`, `DEQUE`, ...) -into an equivalent container but compatible with concurrent access by different threads. -In practice, it puts a mutex lock to access the container. - -As such it is quite generic. However, it is less efficient than containers -specially tuned for multiple threads. -There is also no iterators. - -#### `CONCURRENT_DEF(name, type[, oplist])` -#### `CONCURRENT_DEF_AS(name, name_t, type[, oplist])` - -Define the concurrent container `name` based on container `type` of oplist `oplist`, -and define the associated methods to handle it as `static inline` functions. - -`name` shall be a C identifier that will be used to identify the concurrent container. -It will be used to create all the types (including the iterator) -and functions to handle the container. -This definition shall be done once per name and per compilation unit. - -It scans the `oplist` of the type to create equivalent function, -so the exact generated methods depend on explicitly the exported methods in the oplist. -The init method is only defined if the base container exports the `INIT` operator, -same for the clear method and the `CLEAR` operator, -and so on for all created methods. - -In the description below, -`subtype_t` is the type of the element within the given container `type` (if it exists), -`key_t` is the key type of the element within the given container `type` (if it exists), -`value_t` is the value type of the element within the given container `type` (if it exists). - -`CONCURRENT_DEF_AS` is the same as `CONCURRENT_DEF` except the name of the type `name_t` -is provided. - -Example: - -```C -/* Define a stack container (STACK)*/ -ARRAY_DEF(array1, int) -CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1)) - -/* Define a queue container (FIFO) */ -DEQUE_DEF(deque_uint, unsigned int) -CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_BASIC_OPLIST), PUSH(deque_uint_push_front))) - -extern parray1_t x1; -extern cdeque_uint_t x2; - -void f(void) { - parray1_push (x1, 17); - cdeque_uint_push (x2, 17); -} -``` - -#### Created types - -The following types are automatically defined by the previous definition macro if not provided by the user: - -##### `name_t` - -Type of the concurrent container of `type`. - -#### Common methods - -The following methods of the common interface are defined (See [Common interface](#Common-Interface) for details): - -```C -void name_init(name_t concurrent) -void name_init_set(name_t concurrent, const name_t src) -void name_init_move(name_t concurrent, name_t src) -void name_set(name_t concurrent, const name_t src) -void name_move(name_t concurrent, name_t src) -void name_reset(name_t concurrent) -void name_clear(name_t concurrent) -void name_swap(name_t concurrent1, name_t concurrent2) -bool name_empty_p(const name_t concurrent) -void name_set_at(name_t concurrent, key_t key, value_t value) -bool name_erase(name_t concurrent, const key_t key) -void name_push(name_t concurrent, const subtype_t data) -void name_push_move(name_t concurrent, subtype_t *data) -void name_pop_move(subtype_t *data, name_t concurrent) -void name_get_str(string_t str, name_t concurrent, bool append) -void name_out_str(FILE *file, name_t concurrent) -bool name_parse_str(name_t concurrent, const char str[], const char **end) -bool name_in_str(name_t concurrent, FILE *file) -m_serial_return_code_t name_out_serial(m_serial_write_t serial, const name_t container) -m_serial_return_code_t name_in_str(name_t container, m_serial_read_t serial) -bool name_equal_p(name_t concurrent1, name_t concurrent2) -size_t name_hash(name_t concurrent) -``` - -Returns true in case of success, false otherwise. - -#### Specialized methods - -The following specialized methods are automatically created by the previous definition macro: - -##### `bool name_get_copy(value_t *value, name_t concurrent, key_t key)` - -Read the value associated to the key `key`. -If it exists, it sets `*value` to it and returns true. -Otherwise it returns false (`*value` is unchanged). -This method is only defined if the base container exports the `GET_KEY` operator. - -##### `void name_safe_get_copy(value_t *value, name_t concurrent, key_t key)` - -Read the value associated to the key `key`. -If it exists, it sets `*value` to it. -Otherwise, it creates a new value (default constructor) and sets `*value` to it. -This method is only defined if the base container exports the `SAFE_GET_KEY` operator. - -##### `void name_pop(subtype_t *data, name_t concurrent)` - -Pop data from the container and set it in `*data`. -There shall be at least one data to pop. -Testing with the operator `EMPTY_P` before calling this function is not enough -as there can be some concurrent scenario where another thread pop the last value. -`name_pop_blocking` should be used instead. -This method is only defined if the base container exports the `POP` operator. - -##### `bool name_get_blocking(value_t *value, name_t concurrent, key_t key, bool blocking)` - -Read the value associated to the key `key`. -If it exists, it sets `*value` to it and returns true. -Otherwise, if blocking is true, it waits for the data to be filled. -After the wait, it sets `*value` to it and returns true. -Otherwise, if blocking is false, it returns false. -This method is only defined if the base container exports the `GET_KEY` operator. - -##### `bool name_pop_blocking(type_t *data, name_t concurrent, bool blocking)` - -Pop a value from the container and set `*data` with it. -If the container is not empty, it sets `*data` and return true. -Otherwise, if blocking is true, it waits for the data to be pushed. -After the wait, it sets `*data` to it and returns true. -Otherwise, if blocking is false, it returns false. -This method is only defined if the base container exports the `POP` and `EMPTY_P` operators. - -##### `bool name_pop_move_blocking(type_t *data, name_t concurrent, bool blocking)` - -Pop a value from the container and initialize and set `*data` with it. -If the container is not empty, it initializes and sets `*data` and return true. -Otherwise if blocking is true, it waits for the data to be pushed. -After the wait, it initializes and sets `*data` to it and returns true. -Otherwise, if blocking is false, it returns false. - -> [!WARNING] -> `*data` remains uninitialized! - -This method is only defined if the base container exports the `POP_MOVE` and `EMPTY_P` operators. - -_________________ - ### M-BITSET This header is for using bitset. diff --git a/example/ex11-small-name.c b/example/ex11-small-name.c index 2dac30b2..2a92a3c3 100644 --- a/example/ex11-small-name.c +++ b/example/ex11-small-name.c @@ -8,7 +8,6 @@ #include "m-bptree.h" #include "m-bstring.h" #include "m-buffer.h" -#include "m-concurrent.h" #include "m-deque.h" #include "m-dict.h" #include "m-funcobj.h" diff --git a/m-concurrent.h b/m-concurrent.h deleted file mode 100644 index 53e7ade2..00000000 --- a/m-concurrent.h +++ /dev/null @@ -1,911 +0,0 @@ -/* - * M*LIB - Basic Protected Concurrent module over container. - * - * Copyright (c) 2017-2025, Patrick Pelissier - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * + Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#ifndef MSTARLIB_CONCURRENT_H -#define MSTARLIB_CONCURRENT_H - -#include "m-core.h" -#include "m-thread.h" -#include "m-atomic.h" - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L -#warning The header m-concurrent.h is obsolete. Use m-shared-ptr header instead. -#endif - -/* Define a protected concurrent container and its associated functions - based on the given container. - USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */ -#define M_CONCURRENT_DEF(name, ...) \ - M_CONCURRENT_DEF_AS(name, M_F(name,_t), __VA_ARGS__) - - -/* Define a protected concurrent container and its associated functions - based on the given container as the given name name_t - USAGE: CONCURRENT_DEF_AS(name, name_t, type [, oplist_of_the_type]) */ -#define M_CONCURRENT_DEF_AS(name, name_t, ...) \ - M_BEGIN_PROTECTED_CODE \ - M_C0NCURRENT_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ - ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ - (name, __VA_ARGS__, name_t ))) \ - M_END_PROTECTED_CODE - - -/* Define a protected concurrent container and its associated functions - based on its given container. Operations that perform only read of the container - can be done in parallel. - USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */ -#define M_CONCURRENT_RP_DEF(name, ...) \ - M_CONCURRENT_RP_DEF_AS(name, M_F(name,_t), __VA_ARGS__) - - -/* Define a protected concurrent container and its associated functions - as the given name name_t - based on its given container. Operations that perform only read of the container - can be done in parallel. - USAGE: CONCURRENT_RP_DEF_AS(name, name_t, type [, oplist_of_the_type]) */ -#define M_CONCURRENT_RP_DEF_AS(name, name_t, ...) \ - M_BEGIN_PROTECTED_CODE \ - M_C0NCURRENT_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ - ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \ - (name, __VA_ARGS__, name_t ))) \ - M_END_PROTECTED_CODE - - -/* Define the oplist of a protected concurrent container given its name and its oplist. - USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */ -#define M_CONCURRENT_OPLIST(...) \ - M_C0NCURRENT_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ - ((__VA_ARGS__, M_BASIC_OPLIST), \ - (__VA_ARGS__ ))) - - -/*****************************************************************************/ -/******************************** INTERNAL ***********************************/ -/*****************************************************************************/ - -/* Deferred evaluation for the oplist definition, - so that all arguments are evaluated before further expansion */ -#define M_C0NCURRENT_OPLIST_P1(arg) M_C0NCURRENT_OPLIST_P2 arg - -/* Validation of the given oplist */ -#define M_C0NCURRENT_OPLIST_P2(name, oplist) \ - M_IF_OPLIST(oplist)(M_C0NCURRENT_OPLIST_P3, M_C0NCURRENT_OPLIST_FAILURE)(name, oplist) - -/* Prepare a clean compilation failure */ -#define M_C0NCURRENT_OPLIST_FAILURE(name, oplist) \ - ((M_LIB_ERROR(ARGUMENT_OF_CONCURRENT_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) - -/* OPLIST definition - GET_KEY is not present as its interface is not compatible with a concurrent - container (_get returns a pointer to an internal data, data that may be - destroyed by another thread). -*/ -#define M_C0NCURRENT_OPLIST_P3(name, oplist) \ - (M_IF_METHOD(INIT, oplist)(INIT(M_F(name, _init)),) \ - ,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_F(name, _init_set)),) \ - ,M_IF_METHOD(SET, oplist)(SET(M_F(name, _set)),) \ - ,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_F(name, _clear)),) \ - ,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \ - ,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \ - ,M_IF_METHOD(SWAP,oplist)(SWAP(M_F(name, _swap)),) \ - ,NAME(name) \ - ,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \ - ,SUBTYPE(M_F(name, _subtype_ct)) \ - ,OPLIST(oplist) \ - ,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_F(name,_empty_p)),) \ - ,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_F(name,_size)),) \ - ,M_IF_METHOD(RESET, oplist)(RESET(M_F(name,_reset)),) \ - ,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \ - ,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \ - ,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \ - ,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \ - ,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_F(name, _set_at)),) \ - ,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_F(name, _erase)),) \ - ,M_IF_METHOD(PUSH, oplist)(PUSH(M_F(name,_push)),) \ - ,M_IF_METHOD(POP, oplist)(POP(M_F(name,_pop)),) \ - ,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_F(name,_push_move)),) \ - ,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_F(name,_pop_move)),) \ - ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \ - ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \ - ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \ - ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \ - ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \ - ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \ - ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \ - ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ - ) - - -/******************************** INTERNAL ***********************************/ - -/* Internal contract - NOTE: Can't check too much without locking the container itself -*/ -#define M_C0NCURRENT_CONTRACT(c) do { \ - M_ASSERT ((c) != NULL); \ - M_ASSERT ((c)->self == (c)); \ - } while (0) - -/* Deferred evaluation for the concurrent definition, - so that all arguments are evaluated before further expansion */ -#define M_C0NCURRENT_DEF_P1(arg) M_ID( M_C0NCURRENT_DEF_P2 arg ) - -/* Validate the value oplist before going further */ -#define M_C0NCURRENT_DEF_P2(name, type, oplist, concurrent_t) \ - M_IF_OPLIST(oplist)(M_C0NCURRENT_DEF_P3, M_C0NCURRENT_DEF_FAILURE)(name, type, oplist, concurrent_t) - -/* Stop processing with a compilation failure */ -#define M_C0NCURRENT_DEF_FAILURE(name, type, oplist, concurrent_t) \ - M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) - -/* Internal concurrent definition - - name: prefix to be used - - type: type of the sub container - - oplist: oplist of the type of the sub container - - concurrent_t: alias for M_F(name, _t) [ type of the container ] - */ -#define M_C0NCURRENT_DEF_P3(name, type, oplist, concurrent_t) \ - M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \ - M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ - M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \ - M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) - -/* Define the type of a concurrent container */ -#define M_C0NCURRENT_DEF_TYPE(name, type, oplist, concurrent_t) \ - \ - /* Define a concurrent container using a lock */ \ - typedef struct M_F(name, _s) { \ - struct M_F(name, _s) *self; \ - m_mutex_t lock; \ - m_cond_t there_is_data; /* condition raised when there is data */ \ - type data; \ - } concurrent_t[1]; \ - \ - /* Define alias for pointer types */ \ - typedef struct M_F(name, _s) *M_F(name, _ptr); \ - typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ - \ - /* Internal types for oplist */ \ - typedef concurrent_t M_F(name, _ct); \ - typedef type M_F(name, _subtype_ct); \ - \ - /* Cannot define iterator as it cannot be reliable in a concurrent type */ \ - - /* Define the internal services used for the lock strategy */ -#define M_C0NCURRENT_DEF_CORE(name, type, oplist, concurrent_t) \ - \ - /* Initial the fields of the concurrent object not associated to the \ - sub-container. */ \ - M_INLINE void \ - M_F(name, _internal_init)(concurrent_t out) \ - { \ - m_mutex_init(out->lock); \ - m_cond_init(out->there_is_data); \ - out->self = out; \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - \ - /* Clear the fields of the concurrent object not associated to the \ - sub-container. */ \ - M_INLINE void \ - M_F(name, _internal_clear)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_clear(out->lock); \ - m_cond_clear(out->there_is_data); \ - out->self = NULL; \ - } \ - \ - /* Get the read lock. Multiple threads can get it, but only for reading. \ - write lock is exclusive. \ - NOTE: This instance doesn't implement the read/write strategy, \ - and only get the lock */ \ - M_INLINE void \ - M_F(name, _read_lock)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_lock (out->self->lock); \ - } \ - \ - /* Free the read lock. See above. \ - NOTE: This instance doesn't implement the read/write strategy, \ - and only get the lock */ \ - M_INLINE void \ - M_F(name, _read_unlock)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_unlock (out->self->lock); \ - } \ - \ - /* Wait for a thread pushing some data in the container. \ - CONSTRAINT: the read lock shall be get before calling this service */ \ - M_INLINE void \ - M_F(name, _read_wait)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_cond_wait(out->self->there_is_data, out->self->lock); \ - } \ - \ - /* Get the write lock. Only one threads can get it, and no other threads \ - can get the read lock too. \ - NOTE: This instance doesn't implement the read/write strategy, \ - and only get the lock */ \ - M_INLINE void \ - M_F(name, _write_lock)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_lock (out->lock); \ - } \ - \ - /* Free the write lock. \ - NOTE: This instance doesn't implement the read/write strategy, \ - and only get the lock */ \ - M_INLINE void \ - M_F(name, _write_unlock)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_unlock (out->lock); \ - } \ - \ - /* Wait for a thread pushing some data in the container. \ - CONSTRAINT: the write lock shall be get before calling this service */ \ - M_INLINE void \ - M_F(name, _write_wait)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_cond_wait(out->self->there_is_data, out->self->lock); \ - } \ - \ - /* Wait to all threads that some data are available in the container. \ - CONSTRAINT: the write lock shall be get before calling this service */ \ - M_INLINE void \ - M_F(name, _write_signal)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - /* We need to signal this to ALL waiting threads as multiple threads \ - may wait on a some data of this container. */ \ - m_cond_broadcast(out->there_is_data); \ - } \ - -/* Internal definition of the functions commons to concurrent and rp-concurrent - - name: prefix to be used - - type: type of the sub container - - oplist: oplist of the type of the sub container - - concurrent_t: alias for M_F(name, _t) [ type of the container ] - A function is defined only if the underlying container exports the needed - services. It is usually one service declared per service exported. -*/ -#define M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) \ - \ - M_IF_METHOD(INIT, oplist)( \ - M_INLINE void \ - M_F(name, _init)(concurrent_t out) \ - { \ - M_F(name, _internal_init)(out); \ - M_CALL_INIT(oplist, out->data); \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - ,) \ - \ - M_IF_METHOD(INIT_SET, oplist)( \ - M_INLINE void \ - M_F(name, _init_set)(concurrent_t out, concurrent_t const src) \ - { \ - M_C0NCURRENT_CONTRACT(src); \ - M_ASSERT (out != src); \ - M_F(name, _internal_init)(out); \ - M_F(name, _read_lock)(src); \ - M_CALL_INIT_SET(oplist, out->data, src->data); \ - M_F(name, _read_unlock)(src); \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - ,) \ - \ - M_IF_METHOD(SET, oplist)( \ - M_INLINE void \ - M_F(name, _set)(concurrent_t out, concurrent_t const src) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - if (M_UNLIKELY (out == src)) return; \ - /* Need to order the locks in a total way to avoid lock deadlock. \ - Indeed, two call to _set can be done in two threads with : \ - T1: A := B \ - T2: B := A \ - If we lock first the mutex of out, then the src, it could be possible \ - in the previous scenario that both mutexes are locked: T1 has locked A \ - and T2 has locked B, and T1 is waiting for locking B, and T2 is waiting \ - for locking A, resulting in a deadlock. \ - To avoid this problem, we **always** lock the mutex which address is \ - the lowest. */ \ - if (out < src) { \ - M_F(name, _write_lock)(out); \ - M_F(name, _read_lock)(src); \ - } else { \ - M_F(name, _read_lock)(src); \ - M_F(name, _write_lock)(out); \ - } \ - M_CALL_SET(oplist, out->data, src->data); \ - if (out < src) { \ - M_F(name, _read_unlock)(src); \ - M_F(name, _write_unlock)(out); \ - } else { \ - M_F(name, _write_unlock)(out); \ - M_F(name, _read_unlock)(src); \ - } \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - ,) \ - \ - M_IF_METHOD(CLEAR, oplist)( \ - M_INLINE void \ - M_F(name, _clear)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - /* No need to lock. A clear is supposed to be called when all operations \ - of the container in other threads are terminated */ \ - M_CALL_CLEAR(oplist, out->data); \ - M_F(name, _internal_clear)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(INIT_MOVE, oplist)( \ - M_INLINE void \ - M_F(name, _init_move)(concurrent_t out, concurrent_t src) \ - { \ - M_C0NCURRENT_CONTRACT(src); \ - M_ASSERT (out != src); \ - /* No need to lock 'src' ? */ \ - M_F(name, _internal_init)(out); \ - M_CALL_INIT_MOVE(oplist, out->data, src->data); \ - M_F(name, _internal_clear)(src); \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - ,) \ - \ - M_IF_METHOD(MOVE, oplist)( \ - M_INLINE void \ - M_F(name, _move)(concurrent_t out, concurrent_t src) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_C0NCURRENT_CONTRACT(src); \ - /* No need to lock 'src' ? */ \ - M_F(name, _write_lock)(out); \ - M_CALL_MOVE(oplist, out->data, src->data); \ - M_F(name, _write_unlock)(out); \ - M_F(name, _internal_clear)(src); \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - ,) \ - \ - M_IF_METHOD(SWAP, oplist)( \ - M_INLINE void \ - M_F(name, _swap)(concurrent_t out, concurrent_t src) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_C0NCURRENT_CONTRACT(src); \ - if (M_UNLIKELY (out == src)) return; \ - /* See comment above */ \ - if (out < src) { \ - M_F(name, _write_lock)(out); \ - M_F(name, _write_lock)(src); \ - } else { \ - M_F(name, _write_lock)(src); \ - M_F(name, _write_lock)(out); \ - } \ - M_CALL_SWAP(oplist, out->data, src->data); \ - if (out < src) { \ - M_F(name, _write_unlock)(src); \ - M_F(name, _write_unlock)(out); \ - } else { \ - M_F(name, _write_unlock)(out); \ - M_F(name, _write_unlock)(src); \ - } \ - } \ - ,) \ - \ - M_IF_METHOD(RESET, oplist)( \ - M_INLINE void \ - M_F(name, _reset)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_RESET(oplist, out->data); \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(EMPTY_P, oplist)( \ - M_INLINE bool \ - M_F(name, _empty_p)(concurrent_t const out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - bool b = M_CALL_EMPTY_P(oplist, out->data); \ - M_F(name, _read_unlock)(out); \ - return b; \ - } \ - ,) \ - \ - M_IF_METHOD(GET_SIZE, oplist)( \ - M_INLINE size_t \ - M_F(name, _size)(concurrent_t const out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - size_t r = M_CALL_GET_SIZE(oplist, out->data); \ - M_F(name, _read_unlock)(out); \ - return r; \ - } \ - ,) \ - \ - M_IF_METHOD(SET_KEY, oplist)( \ - M_INLINE void \ - M_F(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_SET_KEY(oplist, out->data, key, data); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(GET_KEY, oplist)( \ - M_INLINE bool \ - M_F(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT (out_data != NULL); \ - M_F(name, _read_lock)(out); \ - M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \ - if (p != NULL) { \ - M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ - } \ - M_F(name, _read_unlock)(out); \ - return p != NULL; \ - } \ - ,) \ - \ - M_IF_METHOD(SAFE_GET_KEY, oplist)( \ - M_INLINE void \ - M_F(name, _safe_get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT (out_data != NULL); \ - M_F(name, _write_lock)(out); \ - M_GET_VALUE_TYPE oplist *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \ - M_ASSERT (p != NULL); \ - M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(ERASE_KEY, oplist)( \ - M_INLINE bool \ - M_F(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \ - /* We suppose that the container has 'infinite' capacity, so \ - we won't signal that a free space has been created */ \ - M_F(name, _write_unlock)(out); \ - return b; \ - } \ - ,) \ - \ - M_IF_METHOD(PUSH, oplist)( \ - M_INLINE void \ - M_F(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_PUSH(oplist, out->data, data); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - } \ - \ - M_EMPLACE_QUEUE_DEF(name, concurrent_t, _emplace, M_GET_OPLIST oplist, M_EMPLACE_QUEUE_GENE) \ - ,) \ - \ - M_IF_METHOD(POP, oplist)( \ - M_INLINE void \ - M_F(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_POP(oplist, p, out->data); \ - /* See comment above */ \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(PUSH_MOVE, oplist)( \ - M_INLINE void \ - M_F(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_PUSH_MOVE(oplist, out->data, data); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(POP_MOVE, oplist)( \ - M_INLINE void \ - M_F(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - M_CALL_POP_MOVE(oplist, p, out->data); \ - /* See comment above */ \ - M_F(name, _write_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(GET_STR, oplist)( \ - M_INLINE void \ - M_F(name, _get_str)(m_string_t str, concurrent_t const out, bool a) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - M_CALL_GET_STR(oplist, str, out->data, a); \ - M_F(name, _read_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(OUT_STR, oplist)( \ - M_INLINE void \ - M_F(name, _out_str)(FILE *f, concurrent_t const out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - M_CALL_OUT_STR(oplist, f, out->data); \ - M_F(name, _read_unlock)(out); \ - } \ - ,) \ - \ - M_IF_METHOD(PARSE_STR, oplist)( \ - M_INLINE bool \ - M_F(name, _parse_str)(concurrent_t out, const char str[], const char **e) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - return b; \ - } \ - ,) \ - \ - M_IF_METHOD(IN_STR, oplist)( \ - M_INLINE bool \ - M_F(name, _in_str)(concurrent_t out, FILE *f) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - bool b = M_CALL_IN_STR(oplist, out->data, f); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - return b; \ - } \ - ,) \ - \ - M_IF_METHOD(OUT_SERIAL, oplist)( \ - M_INLINE m_serial_return_code_t \ - M_F(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \ - M_F(name, _read_unlock)(out); \ - return r; \ - } \ - ,) \ - \ - M_IF_METHOD(IN_SERIAL, oplist)( \ - M_INLINE m_serial_return_code_t \ - M_F(name, _in_serial)(concurrent_t out, m_serial_read_t f) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _write_lock)(out); \ - m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \ - M_F(name, _write_signal)(out); \ - M_F(name, _write_unlock)(out); \ - return r; \ - } \ - ,) \ - \ - M_IF_METHOD(EQUAL, oplist)( \ - M_INLINE bool \ - M_F(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \ - { \ - M_C0NCURRENT_CONTRACT(out1); \ - M_C0NCURRENT_CONTRACT(out2); \ - if (M_UNLIKELY (out1 == out2)) return true; \ - /* See comment above on mutual mutexes */ \ - if (out1 < out2) { \ - M_F(name, _read_lock)(out1); \ - M_F(name, _read_lock)(out2); \ - } else { \ - M_F(name, _read_lock)(out2); \ - M_F(name, _read_lock)(out1); \ - } \ - bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \ - if (out1 < out2) { \ - M_F(name, _read_unlock)(out2); \ - M_F(name, _read_unlock)(out1); \ - } else { \ - M_F(name, _read_unlock)(out1); \ - M_F(name, _read_unlock)(out2); \ - } \ - return b; \ - } \ - ,) \ - \ - M_IF_METHOD(GET_KEY, oplist)( \ - M_INLINE bool \ - M_F(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT (out_data != NULL); \ - bool ret = false; \ - M_F(name, _read_lock)(out); \ - while (true) { \ - M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \ - if (p != NULL) { \ - M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \ - ret = true; \ - break; \ - } \ - if (blocking == false) break; \ - /* No data: wait for a write to signal some data */ \ - M_F(name, _read_wait)(out); \ - } \ - M_F(name, _read_unlock)(out); \ - return ret; \ - } \ - ,) \ - \ - M_IF_METHOD2(POP, EMPTY_P, oplist)( \ - M_INLINE bool \ - M_F(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT (p != NULL); \ - bool ret = false; \ - M_F(name, _write_lock)(out); \ - while (true) { \ - if (!M_CALL_EMPTY_P(oplist, out->data)) { \ - M_CALL_POP(oplist, p, out->data); \ - ret = true; \ - break; \ - } \ - if (blocking == false) break; \ - /* No data: wait for a write to signal some data */ \ - M_F(name, _write_wait)(out); \ - } \ - M_F(name, _write_unlock)(out); \ - return ret; \ - } \ - ,) \ - \ - M_IF_METHOD2(POP_MOVE, EMPTY_P, oplist)( \ - M_INLINE bool \ - M_F(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT (p != NULL); \ - bool ret = false; \ - M_F(name, _write_lock)(out); \ - while (true) { \ - if (!M_CALL_EMPTY_P(oplist, out->data)) { \ - M_CALL_POP_MOVE(oplist, p, out->data); \ - ret = true; \ - break; \ - } \ - if (blocking == false) break; \ - /* No data: wait for a write to signal some data */ \ - M_F(name, _write_wait)(out); \ - } \ - M_F(name, _write_unlock)(out); \ - return ret; \ - } \ - ,) \ - \ - M_IF_METHOD(HASH, oplist)( \ - M_INLINE size_t \ - M_F(name, _hash)(concurrent_t const out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_F(name, _read_lock)(out); \ - size_t h = M_CALL_HASH(oplist, out->data); \ - M_F(name, _read_unlock)(out); \ - /* The hash is unchanged by the concurrent container */ \ - return h; \ - } \ - ,) \ - - -/******************************** INTERNAL ***********************************/ - -/* Deferred evaluation for the RP concurrent definition, - so that all arguments are evaluated before further expansion */ -#define M_C0NCURRENT_RP_DEF_P1(arg) M_ID( M_C0NCURRENT_RP_DEF_P2 arg ) - -/* Validate the value oplist before going further */ -#define M_C0NCURRENT_RP_DEF_P2(name, type, oplist, concurrent_t) \ - M_IF_OPLIST(oplist)(M_C0NCURRENT_RP_DEF_P3, M_C0NCURRENT_RP_DEF_FAILURE)(name, type, oplist, concurrent_t) - -/* Stop processing with a compilation failure */ -#define M_C0NCURRENT_RP_DEF_FAILURE(name, type, oplist, concurrent_t) \ - M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_RP_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist)) - -/* Internal RP concurrent definition - - name: prefix to be used - - type: type of the sub container - - oplist: oplist of the type of the sub container - - concurrent_t: alias for M_F(name, _t) [ type of the container ] - */ -#define M_C0NCURRENT_RP_DEF_P3(name, type, oplist, concurrent_t) \ - M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \ - M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ - M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \ - M_C0NCURRENT_DEF_COMMON(name, type, oplist, concurrent_t) - -/* Define the type of a RP concurrent container */ -#define M_C0NCURRENT_RP_DEF_TYPE(name, type, oplist, concurrent_t) \ - \ - typedef struct M_F(name, _s) { \ - struct M_F(name, _s) *self; \ - m_mutex_t lock; \ - atomic_uint num_reader; /* number of reader threads */ \ - atomic_uint num_waiting_writer; /* number of waiting writer threads */ \ - m_cond_t there_is_data; /* condition raised when there is data */ \ - type data; \ - } concurrent_t[1]; \ - \ - typedef struct M_F(name, _s) *M_F(name, _ptr); \ - typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ - \ - typedef type M_F(name, _subtype_ct); \ - -/* Define the internal services for the lock strategy of a RP container */ -#define M_C0NCURRENT_RP_DEF_CORE(name, type, oplist, concurrent_t) \ - \ - M_INLINE void \ - M_F(name, _internal_init)(concurrent_t out) \ - { \ - m_mutex_init(out->lock); \ - atomic_init(&out->num_reader, 0); \ - atomic_init(&out->num_waiting_writer, 0); \ - m_cond_init(out->there_is_data); \ - out->self = out; \ - M_C0NCURRENT_CONTRACT(out); \ - } \ - \ - M_INLINE void \ - M_F(name, _internal_clear)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_mutex_clear(out->lock); \ - m_cond_clear(out->there_is_data); \ - out->self = NULL; \ - } \ - \ - M_INLINE void \ - M_F(name, _read_lock)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - struct M_F(name, _s) *self = out->self; \ - m_core_backoff_ct backoff; \ - m_core_backoff_init(backoff); \ - while (true) { \ - unsigned int num = atomic_load(&self->num_reader); \ - /* To avoid reader threads that starve writer threads \ - Disable sharing of lock for reader threads if some writer thread \ - waits */ \ - /* Otherwise try to reuse the lock opened by another reader thread */ \ - if (num != 0 \ - && atomic_load(&self->num_waiting_writer) == 0 \ - && atomic_compare_exchange_strong(&self->num_reader, &num, num+1)) { \ - break; \ - } \ - /* Otherwise try to get the lock ourself */ \ - if (m_mutex_trylock(self->lock)) { \ - atomic_store(&self->num_reader, 1); \ - break; \ - } \ - /* Perform exponential backoff to avoid monopolizing the memory bus */ \ - m_core_backoff_wait(backoff); \ - } \ - m_core_backoff_clear(backoff); \ - } \ - \ - M_INLINE void \ - M_F(name, _read_unlock)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - struct M_F(name, _s) *self = out->self; \ - unsigned int num = atomic_fetch_sub(&self->num_reader, 1); \ - if (num == 1) { \ - /* We are the last reader thread: release the lock */ \ - m_mutex_unlock(self->lock); \ - } \ - } \ - \ - M_INLINE void \ - M_F(name, _write_lock)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - atomic_fetch_add(&out->num_waiting_writer, 1); \ - m_mutex_lock (out->lock); \ - atomic_fetch_sub(&out->num_waiting_writer, 1); \ - M_ASSERT(atomic_load(&out->num_reader) == 0); \ - } \ - \ - M_INLINE void \ - M_F(name, _write_unlock)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT(atomic_load(&out->num_reader) == 0); \ - m_mutex_unlock (out->lock); \ - } \ - \ - M_INLINE void \ - M_F(name, _read_wait)(const concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - struct M_F(name, _s) *self = out->self; \ - m_cond_wait(self->there_is_data, self->lock); \ - } \ - \ - M_INLINE void \ - M_F(name, _write_wait)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - m_cond_wait(out->there_is_data, out->lock); \ - } \ - \ - M_INLINE void \ - M_F(name, _write_signal)(concurrent_t out) \ - { \ - M_C0NCURRENT_CONTRACT(out); \ - M_ASSERT(atomic_load(&out->num_reader) == 0); \ - m_cond_broadcast(out->there_is_data); \ - } \ - - -/******************************** INTERNAL ***********************************/ - -#if M_USE_SMALL_NAME -#define CONCURRENT_DEF M_CONCURRENT_DEF -#define CONCURRENT_DEF_AS M_CONCURRENT_DEF_AS -#define CONCURRENT_RP_DEF M_CONCURRENT_RP_DEF -#define CONCURRENT_RP_DEF_AS M_CONCURRENT_RP_DEF_AS -#define CONCURRENT_OPLIST M_CONCURRENT_OPLIST -#endif - -#endif diff --git a/tests/test-mconcurrent.c b/tests/test-mconcurrent.c deleted file mode 100644 index 4c84c509..00000000 --- a/tests/test-mconcurrent.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c) 2017-2025, Patrick Pelissier - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * + Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * + Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#include -#include "test-obj.h" - -#include "m-array.h" -#include "m-bptree.h" -#include "m-deque.h" -#include "m-dict.h" -#include "m-list.h" -#include "m-prioqueue.h" -#include "m-rbtree.h" -#include "m-tuple.h" -#include "m-variant.h" - -#include "m-concurrent.h" -#include "coverage.h" - -TUPLE_DEF2(point, (x, int), (y, int)) -#define M_OPL_point_t() TUPLE_OPLIST(point, M_BASIC_OPLIST, M_BASIC_OPLIST) -START_COVERAGE -CONCURRENT_DEF(ppoint, point_t) -END_COVERAGE - -VARIANT_DEF2(dimension, (x, int), (y, float)) -#define M_OPL_dimension_t() VARIANT_OPLIST(dimension, M_BASIC_OPLIST, M_BASIC_OPLIST) -CONCURRENT_DEF(pdimension, dimension_t) - -ARRAY_DEF(array1, int) -CONCURRENT_DEF(parray1, array1_t, ARRAY_OPLIST(array1)) - -DICT_DEF2(dict1, int, int) -CONCURRENT_DEF(pdict1, dict1_t, DICT_OPLIST(dict1)) - -DICT_SET_DEF(dict2, int) -CONCURRENT_DEF(pdict2, dict2_t, DICT_SET_OPLIST(dict2)) - -LIST_DEF(list1, int) -CONCURRENT_DEF(plist1, list1_t, LIST_OPLIST(list1)) - -LIST_DUAL_PUSH_DEF(list2, int) -CONCURRENT_DEF(plist2, list2_t, LIST_OPLIST(list2)) - -DEQUE_DEF(deque1, int) -CONCURRENT_DEF(pdeque1, deque1_t, DEQUE_OPLIST(deque1)) - -PRIOQUEUE_DEF(prio1, int) -CONCURRENT_DEF(pprio1, prio1_t, PRIOQUEUE_OPLIST(prio1)) - -BPTREE_DEF2(bptree1, 10, int, int) -CONCURRENT_DEF(pbtree1, bptree1_t, BPTREE_OPLIST2(bptree1, M_BASIC_OPLIST, M_BASIC_OPLIST)) - -BPTREE_DEF(bptree2, 10, int) -CONCURRENT_DEF(pbtree2, bptree2_t, BPTREE_OPLIST(bptree2)) - -RBTREE_DEF(rbtree1, int) -CONCURRENT_DEF(prbtree1, rbtree1_t, RBTREE_OPLIST(rbtree1)) - -DICT_DEF2(string_pool, string_t, string_t) -#define STRING_POOL_OPLIST \ - DICT_OPLIST(string_pool, STRING_OPLIST, STRING_OPLIST) -#define M_OPL_string_pool_t() STRING_POOL_OPLIST -CONCURRENT_DEF(string_pool_ts, string_pool_t, STRING_POOL_OPLIST) -#define STRING_POOL_TS_OPLIST \ - CONCURRENT_OPLIST(string_pool_ts, STRING_POOL_OPLIST) -#define M_OPL_string_pool_ts_t() STRING_POOL_TS_OPLIST - -TUPLE_DEF2(my_tuple, (pool, string_pool_ts_t)) -#define MY_TUPLE_OPLIST TUPLE_OPLIST(my_tuple, STRING_POOL_TS_OPLIST) -#define M_OPL_my_tuple_t() MY_TUPLE_OPLIST - -ARRAY_DEF(key_list, string_t) -#define M_OPL_key_list_t() ARRAY_OPLIST(key_list, STRING_OPLIST) -CONCURRENT_DEF(key_list_ts, key_list_t) - -CONCURRENT_RP_DEF(rparray1, array1_t, ARRAY_OPLIST(array1)) - -CONCURRENT_RP_DEF(rpdict1, dict1_t, DICT_OPLIST(dict1)) - -CONCURRENT_DEF_AS(ConcurrentDouble, ConcurrentDouble, double) -CONCURRENT_RP_DEF_AS(ConcurrentRpDouble, ConcurrentRpDouble, double) - -/* OA dict needs more operators than the default ones to work */ -static inline bool int_oor_equal_p(int s, unsigned char n) -{ - return s == -n; -} -static inline void int_oor_set(int *s, unsigned char n) -{ - *s = -n; -} -#define INT_OA_OPLIST \ - M_OPEXTEND(M_BASIC_OPLIST, OOR_EQUAL(int_oor_equal_p), OOR_SET(API_2(int_oor_set))) -DICT_OA_DEF2(dict3, int, INT_OA_OPLIST, int, M_BASIC_OPLIST) -CONCURRENT_DEF(pdict3, dict3_t, DICT_OPLIST(dict3)) - -/********************************/ -parray1_t arr; - -static void conso(void *p) -{ - assert (p == NULL); - for(int i = 0; i < 1000; i++) { - int j; - bool b = parray1_pop_blocking(&j, arr, true); - assert (b); - assert (j == i); - } -} - -static void test_thread(void) -{ - m_thread_t idx; - parray1_init(arr); - m_thread_create (idx, conso, NULL); - m_thread_sleep(1000); - for(int i = 0; i < 1000; i++) { - parray1_push (arr, i); - while (!parray1_empty_p(arr)); - } - m_thread_join(idx); - parray1_clear(arr); -} - -static void test_basic(void) -{ - pdict1_t dict; - pdict1_init(dict); - int z; - pdict1_set_at(dict, 2, 3); - pdict1_get_copy(&z, dict, 2); - assert (z == 3); - z = 1; - bool b = pdict1_get_blocking(&z, dict, 2, false); - assert (b); - assert (z == 3); - assert (pdict1_size(dict) == 1); - pdict1_clear(dict); - - M_LET(str, string_t) - M_LET(pool, string_pool_ts_t) { - string_pool_ts_set_at (pool, STRING_CTE("A"), STRING_CTE("B")); - b = string_pool_ts_get_copy (&str, pool, STRING_CTE("A")); - assert (b); - assert (string_equal_str_p (str, "B")); - } - M_LET(tuple, my_tuple_t) { - assert (string_pool_ts_empty_p(tuple->pool)); - } -} - - -rparray1_t rarr; - -static void conso_rp(void *p) -{ - assert (p == NULL); - for(int i = 0; i < 1000; i++) { - int j; - bool b = rparray1_pop_blocking(&j, rarr, true); - assert (b); - assert (j == i); - } -} - -static void test_rp_thread(void) -{ - m_thread_t idx; - rparray1_init(rarr); - m_thread_create (idx, conso_rp, NULL); - m_thread_sleep(1000); - for(int i = 0; i < 1000; i++) { - rparray1_push (rarr, i); - while (!rparray1_empty_p(rarr)); - } - m_thread_join(idx); - rparray1_clear(rarr); -} - -static void test_rp_basic(void) -{ - rpdict1_t dict; - rpdict1_init(dict); - int z = 0; - rpdict1_set_at(dict, 2, 3); - rpdict1_get_copy(&z, dict, 2); - assert (z == 3); - z = 1; - bool b = rpdict1_get_blocking(&z, dict, 2, false); - assert (b); - assert (z == 3); - assert (rpdict1_size(dict) == 1); - rpdict1_clear(dict); -} - -static void test_double(void) -{ - ConcurrentDouble d; - ConcurrentDouble_init(d); - ConcurrentDouble_reset(d); - ConcurrentDouble_clear(d); - - ConcurrentRpDouble r; - ConcurrentRpDouble_init(r); - ConcurrentRpDouble_reset(r); - ConcurrentRpDouble_clear(r); -} - -static void test_emplace(void) -{ - key_list_ts_t ts; - key_list_ts_init(ts); - key_list_ts_emplace(ts, "HELLO"); - M_LET(s, string_t) { - key_list_ts_pop(&s, ts); - assert(string_equal_str_p(s, "HELLO")); - } - key_list_ts_clear(ts); -} - -int main(void) -{ - test_basic(); - test_rp_basic(); - test_thread(); - test_rp_thread(); - test_double(); - test_emplace(); - exit(0); -}