Files
mlib/m-shared-ptr.h
T
2024-11-02 23:35:21 +01:00

1271 lines
92 KiB
C

/*
* M*LIB - Shared Pointer container providing encapsulation and protected concurrent access.
*
* Copyright (c) 2017-2024, 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_SHARED_PTR2_H
#define MSTARLIB_SHARED_PTR2_H
#include "m-core.h"
#include "m-atomic.h"
#include "m-thread.h"
/* Declare a shared weak pointer (not atomic, single thread) for use in header file.
oplist is mandatory but is only used to test if a function has to be declared */
#define M_SHARED_WEAK_PTR_DECL(name, oplist) \
M_SHAR3D_PTR_DECL_P2(name, M_F(name, _t), oplist)
/* Declare a shared weak pointer (not atomic, single thread) for use in header file.
oplist is mandatory but is only used to test if a function has to be declared */
#define M_SHARED_WEAK_PTR_DECL_AS(name, shared_t, oplist) \
M_SHAR3D_PTR_DECL_P2(name, shared_t, oplist)
/* Declare a shared strong pointer (atomic & lock) for use in header file.
oplist is mandatory but is only used to test if a function has to be declared */
#define M_SHARED_PTR_DECL(name, oplist) \
M_SHAR3D_PTR_DECL_P2(name, M_F(name, _t), oplist)
/* Declare a shared strong pointer (atomic & lock) for use in header file.
oplist is mandatory but is only used to test if a function has to be declared */
#define M_SHARED_PTR_DECL_AS(name, shared_t, oplist) \
M_SHAR3D_PTR_DECL_P2(name, shared_t, oplist)
/* Define an external definition a shared weak pointer (not atomic). oplist is optional */
#define M_SHARED_WEAK_PTR_DEF_EXTERN(name, ...) \
M_SHARED_WEAK_PTR_DEF_EXTERN_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define an external definition a shared weak pointer (not atomic). oplist is optional */
#define M_SHARED_WEAK_PTR_DEF_EXTERN_AS(name, shared_t, ...) \
M_SHAR3D_WEAK_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, shared_t, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), /*extern*/ ), \
(name, shared_t, __VA_ARGS__, /*extern*/ )))
/* Define an external definition a shared strong pointer (atomic & lock). oplist is optional */
#define M_SHARED_PTR_DEF_EXTERN(name, ...) \
M_SHARED_PTR_DEF_EXTERN_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define an external definition a shared strong pointer (atomic & lock). oplist is optional */
#define M_SHARED_PTR_DEF_EXTERN_AS(name, shared_t, ...) \
M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, shared_t, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), /*extern*/ ), \
(name, shared_t, __VA_ARGS__, /*extern*/ )))
/* Define a static inline a shared weak pointer (not atomic). oplist is optional */
#define M_SHARED_WEAK_PTR_DEF(name, ...) \
M_SHARED_WEAK_PTR_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define a static inline a shared weak pointer (not atomic). oplist is optional */
#define M_SHARED_WEAK_PTR_DEF_AS(name, shared_t, ...) \
M_SHARED_PTR_DECL_TYPE(name, shared_t, ) \
M_SHAR3D_WEAK_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, shared_t, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), static inline ), \
(name, shared_t, __VA_ARGS__, static inline )))
/* Define a static inline a shared strong pointer (atomic & lock). oplist is optional */
#define M_SHARED_PTR_DEF(name, ...) \
M_SHARED_PTR_DEF_AS(name, M_F(name, _t), __VA_ARGS__)
/* Define a static inline a shared strong pointer (atomic & lock). oplist is optional */
#define M_SHARED_PTR_DEF_AS(name, shared_t, ...) \
M_SHARED_PTR_DECL_TYPE(name, shared_t, ) \
M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, shared_t, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), static inline ), \
(name, shared_t, __VA_ARGS__, static inline )))
/* Define the oplist of a shared pointer given its name and its oplist.
Oplist is mandatory.
USAGE: SHARED_PTR_OPLIST(name, oplist of the type) */
#define M_SHARED_PTR_OPLIST(...) \
M_SHAR3D_OPLIST_P1 ((__VA_ARGS__ ))
/* Define the oplist of a shared pointer as a shared data given its name and its oplist.
Oplist is mandatory.
USAGE: SHARED_DATA_OPLIST(name, oplist of the type) */
#define M_SHARED_DATA_OPLIST(...) \
M_SHAR3D_DATA_OPLIST_P1 ((__VA_ARGS__ ))
/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_SHAR3D_OPLIST_P1(arg) M_SHAR3D_OPLIST_P2 arg
/* Validation of the given oplist */
#define M_SHAR3D_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_SHAR3D_OPLIST_P3, M_SHAR3D_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_SHAR3D_OPLIST_FAILURE(name, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_SHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of a shared pointer */
#define M_SHAR3D_OPLIST_P3(name, oplist) \
(NAME(name), TYPE(struct M_C(name,_s) *) \
,M_IF_METHOD(INIT)(INIT(API_4(M_F(name, _new))),) \
,INIT_SET(API_4(M_F(name, _acquire))) \
,SET(API_2(M_F(name, _set))) \
,CLEAR(M_F(name, _release)) \
,INIT_MOVE(M_SET_BASIC) \
,MOVE(M_SET_BASIC) \
)
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_SHAR3D_DATA_OPLIST_P1(arg) M_SHAR3D_DATA_OPLIST_P2 arg
/* Validation of the given oplist */
#define M_SHAR3D_DATA_OPLIST_P2(name, oplist) \
M_IF_OPLIST(oplist)(M_SHAR3D_DATA_OPLIST_P3, M_SHAR3D_DATA_OPLIST_FAILURE)(name, oplist)
/* Prepare a clean compilation failure */
#define M_SHAR3D_DATA_OPLIST_FAILURE(name, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_SHARED_DATA_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* OPLIST definition of the shared data of a shared pointer */
#define M_SHAR3D_DATA_OPLIST_P3(name, oplist) \
(NAME(name), TYPE(struct M_C(name,_s) *) \
,M_IF_METHOD(INIT)(INIT(API_4(M_F(name, _new))),) \
,M_IF_METHOD(INIT_SET)(INIT_SET(API_4(M_F(name, _new_copy))),) \
,M_IF_METHOD(SET)(SET(API_4(M_F(name, _copy))),) \
,CLEAR(M_F(name, _clear)) \
,INIT_MOVE(M_SET_BASIC) \
,MOVE(M_SET_BASIC) \
)
/* Validation of the given oplist */
#define M_SHAR3D_PTR_DECL_P2(name, shared_t, oplist) \
M_IF_OPLIST(oplist)(M_SHAR3D_PTR_DECL_P3, M_SHAR3D_PTR_DECL_FAILURE)(name, shared_t, oplist)
/* Prepare a clean compilation failure */
#define M_SHAR3D_PTR_DECL_FAILURE(name, shared_t, oplist) \
((M_LIB_ERROR(ARGUMENT_OF_DECL_SHARED_POINTER_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* Declare all functions assiated to the shared pointer */
#define M_SHAR3D_PTR_DECL_P3(name, shared_t, oplist) \
M_BEGIN_PROTECTED_CODE \
M_SHARED_PTR_DECL_TYPE(name, shared_t, oplist) \
M_SHARED_PTR_DECL_BASIC(name, shared_t, oplist) \
M_SHARED_PTR_DECL_EXTRA(name, shared_t, oplist) \
M_SHARED_PTR_DECL_ARITH(name, shared_t, oplist) \
M_SHARED_PTR_DECL_KEY(name, shared_t, oplist, M_GET_KEY_TYPE oplist, M_GET_VALUE_TYPE oplist) \
M_SHARED_PTR_DECL_PUSH(name, shared_t, oplist, M_GET_SUBTYPE oplist) \
M_SHARED_PTR_DECL_POP(name, shared_t, oplist, M_GET_SUBTYPE oplist) \
M_SHARED_PTR_DECL_IT(name, shared_t, oplist, M_GET_SUBTYPE oplist) \
M_SHARED_PTR_DECL_IO(name, shared_t, oplist) \
M_END_PROTECTED_CODE
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_SHAR3D_WEAK_PTR_DEF_P1(arg) M_SHAR3D_WEAK_PTR_DEF_P2 arg
/* Validation of the given oplist */
#define M_SHAR3D_WEAK_PTR_DEF_P2(name, shared_t, type, oplist, fattr) \
M_IF_OPLIST(oplist)(M_SHAR3D_WEAK_PTR_DEF_P3, M_SHAR3D_WEAK_PTR_DEF_FAILURE)(name, shared_t, type, oplist, fattr)
/* Prepare a clean compilation failure */
#define M_SHAR3D_WEAK_PTR_DEF_FAILURE(name, shared_t, type, oplist, fattr) \
((M_LIB_ERROR(ARGUMENT_OF_WEAK_DEF_SHARED_POINTER_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* Define all functions assiated to the weak shared pointer as 'fattr' (can be static inline or extern) */
#define M_SHAR3D_WEAK_PTR_DEF_P3(name, shared_t, type, oplist, fattr) \
M_BEGIN_PROTECTED_CODE \
M_SHARED_PTR_NO_THRD_DEF_TYPE(name, shared_t, type, oplist) \
M_SHARED_PTR_NO_THRD_DEF_CORE(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_BASIC(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_EXTRA(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_ARITH(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_KEY(name, shared_t, type, oplist, fattr, M_GET_KEY_TYPE oplist, M_GET_KEY_OPLIST oplist, M_GET_VALUE_TYPE oplist, M_GET_VALUE_OPLIST oplist) \
M_SHARED_PTR_DEF_PUSH(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_POP(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_IT(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_IO(name, shared_t, type, oplist, fattr) \
M_END_PROTECTED_CODE
/* Deferred evaluation for the oplist definition,
so that all arguments are evaluated before further expansion */
#define M_SHAR3D_PTR_DEF_P1(arg) M_SHAR3D_PTR_DEF_P2 arg
/* Validation of the given oplist */
#define M_SHAR3D_PTR_DEF_P2(name, shared_t, type, oplist, fattr) \
M_IF_OPLIST(oplist)(M_SHAR3D_PTR_DEF_P3, M_SHAR3D_PTR_DEF_FAILURE)(name, shared_t, type, oplist, fattr)
/* Prepare a clean compilation failure */
#define M_SHAR3D_PTR_DEF_FAILURE(name, shared_t, type, oplist, fattr) \
((M_LIB_ERROR(ARGUMENT_OF_DEF_SHARED_POINTER_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
/* Define all functions assiated to the shared pointer as 'fattr' (can be static inline or extern) */
#define M_SHAR3D_PTR_DEF_P3(name, shared_t, type, oplist, fattr) \
M_BEGIN_PROTECTED_CODE \
M_SHARED_PTR_DEF_TYPE(name, shared_t, type, oplist) \
M_SHARED_PTR_DEF_CORE(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_BASIC(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_EXTRA(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_ARITH(name, shared_t, type, oplist, fattr) \
M_SHARED_PTR_DEF_KEY(name, shared_t, type, oplist, fattr, M_GET_KEY_TYPE oplist, M_GET_KEY_OPLIST oplist, M_GET_VALUE_TYPE oplist, M_GET_VALUE_OPLIST oplist) \
M_SHARED_PTR_DEF_PUSH(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_POP(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_IT(name, shared_t, type, oplist, fattr, M_GET_SUBTYPE oplist, M_GET_OPLIST oplist) \
M_SHARED_PTR_DEF_IO(name, shared_t, type, oplist, fattr) \
M_END_PROTECTED_CODE
/* Declare the type of shared pointer. Opaque structure for encapsulation */
#define M_SHARED_PTR_DECL_TYPE(name, shared_t, oplist) \
typedef struct M_C(name, _s) shared_t;
/* Definition of the type with no thread safety (single thread) */
#define M_SHARED_PTR_NO_THRD_DEF_TYPE(name, shared_t, type, oplist) \
struct M_C(name, _s) { \
type data; /* Allow safe casting from shared_t* to type* */ \
unsigned cpt; /* Owner counter that acquire the data */ \
};
/* Define the core functions without any thread safety */
#define M_SHARED_PTR_NO_THRD_DEF_CORE(name, shared_t, type, oplist, fattr) \
/* Increment the number of owner (acquire the resource) */ \
fattr void M_F(name, _inc_owner)(shared_t *out) \
{ \
out->cpt ++; \
} \
/* Decrement the number of owner (release the resource). Return true if it was the last owner */ \
fattr bool M_F(name, _dec_owner)(shared_t *out) \
{ \
out->cpt --; \
return 0 == out->cpt; \
} \
/* Init the lock */ \
fattr void M_F(name, _init_lock)(shared_t *out) \
{ \
out->cpt = 0; \
} \
/* Clear the lock */ \
fattr void M_F(name, _clear_lock)(shared_t *out) \
{ \
M_ASSERT(out->cpt == 0); \
(void) out; \
} \
/* Enter the lock for reading the data */ \
fattr void M_F(name, _read_lock)(const shared_t *out) \
{ \
(void) out; \
} \
/* wait for some new data to be available (within read_lock) */ \
fattr void M_F(name, _read_wait)(const shared_t *out) \
{ \
(void) out; \
} \
/* Leave the lock for reading the data (within read_lock) */ \
fattr void M_F(name, _read_unlock)(const shared_t *out) \
{ \
(void) out; \
} \
/* Enter the lock for writing/updating the data */ \
fattr void M_F(name, _write_lock)(shared_t *out) \
{ \
(void) out; \
} \
/* wait to be able to write new data (within write_lock) */ \
fattr void M_F(name, _write_wait)(shared_t *out) \
{ \
(void) out; \
} \
/* signal some new data are available (within write_lock) */ \
fattr void M_F(name, _write_signal)(shared_t *out) \
{ \
(void) out; \
} \
/* signal some free slot are available (within write_lock) */ \
fattr void M_F(name, _free_signal)(shared_t *out) \
{ \
(void) out; \
} \
/* Leaver the lock for writing/updating the data (within write_lock) */ \
fattr void M_F(name, _write_unlock)(shared_t *out) \
{ \
(void) out; \
} \
/* Return a pointer to the data */ \
fattr type *M_F(name, _ref)(shared_t *out) \
{ \
return &out->data; \
} \
/* Return a const pointer to the data */ \
fattr type const *M_F(name, _cref)(const shared_t *out) \
{ \
return &out->data; \
} \
/* Perform a write lock on out and a read lock on src */ \
fattr void M_F(name, _write_read_lock)(shared_t *out, const shared_t *src) \
{ \
(void) out; \
(void) src; \
} \
/* Leave the write lock on out and the read lock on src */ \
fattr void M_F(name, _write_read_unlock)(shared_t *out, const shared_t *src) \
{ \
(void) out; \
(void) src; \
} \
/* Definition of the type with thread safety. */
//TODO: If PROPERTIES.THREADSAFE, disable the global lock as the container handles it itself.
//FIXME: Such a property may need to be more fine tuned than globaly.
#define M_SHARED_PTR_DEF_TYPE(name, shared_t, type, oplist) \
struct M_C(name, _s) { \
type data; /* Allow safe casting from shared_t* to type* */ \
atomic_uint cpt; /* Owner counter that acquire the data */ \
m_mutex_t lock; /* Lock to modify the data */ \
atomic_uint num_reader; /* Number of reader threads that read the data */ \
atomic_uint num_waiting_writer; /* Number of waiting writing threads that wants to modify data */ \
m_cond_t there_is_data; /* cond var sent if there a new data added */ \
M_IF_METHOD(FULL_P, oplist)( /* cond var has meaning if it can be full */ \
m_cond_t there_is_slot; /* cond var sent if there is a free slot */ \
, ) \
};
/* Define the core functions with thread safety
NOTE: lock are exception free. */
#define M_SHARED_PTR_DEF_CORE(name, shared_t, type, oplist, fattr) \
\
fattr void M_F(name, _inc_owner)(shared_t *out) \
{ \
atomic_fetch_add(&out->cpt, 1); \
} \
\
fattr bool M_F(name, _dec_owner)(shared_t *out) \
{ \
return 1 == atomic_fetch_sub(&out->cpt, 1); \
} \
\
fattr void M_F(name, _init_lock)(shared_t *out) \
{ \
atomic_init(&out->cpt, 0); \
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); \
M_IF_METHOD(FULL_P, oplist)(m_cond_init(out->there_is_slot), (void)0); \
} \
\
fattr void M_F(name, _clear_lock)(shared_t *out) \
{ \
m_mutex_clear(out->lock); \
m_cond_clear(out->there_is_data); \
M_IF_METHOD(FULL_P, oplist)(m_cond_clear(out->there_is_slot), (void)0); \
} \
\
fattr void M_F(name, _read_lock)(const shared_t *out) \
{ \
shared_t *self = (shared_t *)out; \
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_LIKELY(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); \
} \
\
fattr void M_F(name, _read_wait)(const shared_t *out) \
{ \
shared_t *self = (shared_t *)out; \
m_cond_wait(self->there_is_data, self->lock); \
} \
\
fattr void M_F(name, _read_unlock)(const shared_t *out) \
{ \
shared_t *self = (shared_t *)out; \
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); \
} \
} \
\
fattr void M_F(name, _write_lock)(shared_t *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); \
} \
\
fattr void M_F(name, _write_wait)(shared_t *out) \
{ \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
M_IF_METHOD(FULL_P, oplist)( \
m_cond_wait(out->there_is_slot, out->lock); \
,) \
} \
\
fattr void M_F(name, _write_signal)(shared_t *out) \
{ \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
m_cond_broadcast(out->there_is_data); \
} \
\
fattr void M_F(name, _free_signal)(shared_t *out) \
{ \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
M_IF_METHOD(FULL_P, oplist)( \
m_cond_broadcast(out->there_is_slot); \
, ) \
} \
\
fattr void M_F(name, _write_unlock)(shared_t *out) \
{ \
M_ASSERT(atomic_load(&out->num_reader) == 0); \
m_mutex_unlock (out->lock); \
} \
\
fattr type *M_F(name, _ref)(shared_t *out) \
{ \
return &out->data; \
} \
\
fattr type const *M_F(name, _cref)(const shared_t *out) \
{ \
return &out->data; \
} \
\
fattr void M_F(name, _write_read_lock)(shared_t *out, const shared_t *src) \
{ \
/* 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. \
Note that is technically an undefined behavior of the C standard */ \
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); \
} \
} \
\
fattr void M_F(name, _write_read_unlock)(shared_t *out, const shared_t *src) \
{ \
/* NOTE: No need to order the unlock */ \
M_F(name, _read_unlock)(src); \
M_F(name, _write_unlock)(out); \
} \
/* Define the basic function of a shared pointer */
#define M_SHARED_PTR_DECL_BASIC(name, shared_t, oplist) \
M_IF_METHOD(INIT, oplist)( extern shared_t *M_F(name, _new)(void); , ) \
M_IF_METHOD(INIT_SET, oplist)( extern shared_t *M_F(name, _new_copy)(const shared_t *); , ) \
M_IF_METHOD(SET, oplist)( extern void M_F(name, _copy)(shared_t *, const shared_t *); , ) \
extern shared_t *M_F(name, _acquire)(shared_t *); \
extern void M_F(name, _release)(shared_t *); \
extern void M_F(name, _set)(shared_t **, shared_t *); \
extern void M_F(name, _clear)(shared_t *); \
M_EMPLACE_QUEUE_DEF(name, shared_t, M_F(name, _make), oplist, M_SHARED_PTR_DECL_BASIC_MAKE)
#define M_SHARED_PTR_DECL_BASIC_MAKE(name, shared_t, function_name, oplist, init_func, exp_emplace_type) \
extern shared_t *function_name(M_EMPLACE_LIST_TYPE_VAR_ALTER(a, exp_emplace_type));
#define M_SHARED_PTR_DEF_BASIC(name, shared_t, type, oplist, fattr) \
M_IF_METHOD(INIT, oplist)( \
fattr shared_t *M_F(name, _new)(void) \
{ \
shared_t *out = M_CALL_NEW(oplist, shared_t); \
if (M_UNLIKELY_NOMEM( out == NULL)) { \
M_MEMORY_FULL(sizeof (shared_t)); \
abort(); \
} \
M_ON_EXCEPTION( M_CALL_FREE(oplist, out) ) \
M_CALL_INIT(oplist, out->data); \
M_F(name, _init_lock)(out); \
return out; \
} \
, ) \
\
M_IF_METHOD(INIT_SET, oplist)( \
fattr shared_t *M_F(name, _new_copy)(const shared_t *src) \
{ \
M_ASSERT(src != NULL); \
shared_t *out = M_CALL_NEW(oplist, shared_t); \
if (M_UNLIKELY_NOMEM( out == NULL)) { \
M_MEMORY_FULL(sizeof (shared_t)); \
abort(); \
} \
M_F(name, _read_lock)(src); \
M_ON_EXCEPTION( M_F(name, _read_unlock)(src), M_CALL_FREE(oplist, out) ) \
M_CALL_INIT_SET(oplist, out->data, src->data); \
M_F(name, _read_unlock)(src); \
M_F(name, _init_lock)(out); \
return out; \
} \
, ) \
\
M_IF_METHOD(SET, oplist)( \
fattr void M_F(name, _copy)(shared_t *out, const shared_t *src) \
{ \
M_ASSERT(out != NULL); \
M_ASSERT(src != NULL); \
if (M_UNLIKELY (out == src)) return; \
M_F(name, _write_read_lock)(out, src); \
M_ON_EXCEPTION( M_F(name, _write_read_unlock)(out, src) ) \
M_CALL_SET(oplist, out->data, src->data); \
M_F(name, _write_signal)(out); \
M_F(name, _write_read_unlock)(out, src); \
} \
, ) \
\
fattr shared_t *M_F(name, _acquire)(shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _inc_owner)(out); \
return out; \
} \
\
fattr void M_F(name, _release)(shared_t *out) \
{ \
if (out != NULL && M_F(name, _dec_owner)(out)) { \
M_CALL_CLEAR(oplist, out->data); \
M_F(name, _clear_lock)(out); \
M_CALL_DEL(oplist, out); \
} \
} \
\
fattr void M_F(name, _set)(shared_t **dst, shared_t *out) \
{ \
M_ASSERT(dst != NULL && out != NULL); \
M_F(name, _release)(*dst); \
*dst = M_F(name, _acquire)(out); \
} \
\
fattr void M_F(name, _clear)(shared_t *out) \
{ \
M_F(name, _release)(out); \
} \
M_EMPLACE_QUEUE_DEF( (name, fattr), shared_t, M_F(name, _make), oplist, M_SHARED_PTR_DEF_BASIC_MAKE)
#define M_SHARED_PTR_DEF_BASIC_MAKE(name_attr, shared_t, function_name, oplist, init_func, exp_emplace_type) \
M_PAIR_2 name_attr shared_t *function_name(M_EMPLACE_LIST_TYPE_VAR_ALTER(a, exp_emplace_type)) \
{ \
shared_t *out = M_CALL_NEW(oplist, shared_t); \
if (M_UNLIKELY_NOMEM( out == NULL)) { \
M_MEMORY_FULL(sizeof (shared_t)); \
abort(); \
} \
M_ON_EXCEPTION( M_CALL_FREE(oplist, out) ) \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, out->data, exp_emplace_type); \
M_F(M_PAIR_1 name_attr, _init_lock)(out); \
return out; \
} \
/* Define the extra function of a shared pointer */
#define M_SHARED_PTR_DECL_EXTRA(name, shared_t, oplist) \
M_IF_METHOD(SWAP, oplist)( extern void M_F(name, _swap)(shared_t *, shared_t *); , ) \
M_IF_METHOD(RESET, oplist)( extern void M_F(name, _reset)(shared_t *); , ) \
M_IF_METHOD(EMPTY_P, oplist)( extern bool M_F(name, _empty_p)(const shared_t *); , ) \
M_IF_METHOD(GET_SIZE, oplist)( extern size_t M_F(name, _size)(const shared_t *); , ) \
M_IF_METHOD(EQUAL, oplist)( extern bool M_F(name, _equal_p)(const shared_t *, const shared_t *); , ) \
M_IF_METHOD(CMP, oplist)( extern int M_F(name, _cmp)(const shared_t *, const shared_t *); , ) \
M_IF_METHOD(HASH, oplist)( extern size_t M_F(name, _hash)(const shared_t *); , ) \
#define M_SHARED_PTR_DEF_EXTRA(name, shared_t, type, oplist, fattr) \
M_IF_METHOD(SWAP, oplist)( \
fattr void M_F(name, _swap)(shared_t *o1, shared_t *o2) \
{ \
M_ASSERT(o1 != NULL && o2 != NULL); \
if (M_UNLIKELY (o1 == o2)) return; \
/* See comment above on _write_read_lock */ \
if (o1 > o2) { \
M_SWAP(shared_t *, o1, o2); \
} \
M_F(name, _write_lock)(o1); \
M_F(name, _write_lock)(o2); \
/* SWAP cannot throw exception */ \
M_CALL_SWAP(oplist, o1->data, o2->data); \
/* It doesn't make sense to signal new data or new slot, Right? */ \
M_F(name, _write_unlock)(o1); \
M_F(name, _write_unlock)(o2); \
} \
, ) \
\
M_IF_METHOD(RESET, oplist)( \
fattr void M_F(name, _reset)(shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _write_lock)(out); \
/* RESET cannot throw exception */ \
M_CALL_RESET(oplist, out->data); \
M_F(name, _free_signal)(out); \
M_F(name, _write_unlock)(out); \
} \
, ) \
\
M_IF_METHOD(EMPTY_P, oplist)( \
fattr bool M_F(name, _empty_p)(const shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _read_lock)(out); \
/* EMPTY_P cannot throw exception */ \
bool r = M_CALL_EMPTY_P(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
, ) \
\
M_IF_METHOD(FULL_P, oplist)( \
fattr bool M_F(name, _full_p)(const shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _read_lock)(out); \
/* FULL_P cannot throw exception */ \
bool r = M_CALL_FULL_P(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
, ) \
\
M_IF_METHOD(GET_SIZE, oplist)( \
fattr size_t M_F(name, _size)(const shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _read_lock)(out); \
/* GET_SIZE cannot throw exception */ \
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
, ) \
\
M_IF_METHOD(EQUAL, oplist)( \
fattr bool M_F(name, _equal_p)(const shared_t *o1, const shared_t *o2) \
{ \
M_ASSERT(o1 != NULL && o2 != NULL); \
/* See comment above on _write_read_lock */ \
if (o1 > o2) { \
M_SWAP(const shared_t *, o1, o2); \
} \
/* FIXME: OOR compare and constraint on OOR argument? */ \
M_F(name, _read_lock)(o1); \
M_F(name, _read_lock)(o2); \
/* EQUAL cannot throw exception */ \
bool r = M_CALL_EQUAL(oplist, o1->data, o2->data); \
M_F(name, _read_unlock)(o1); \
M_F(name, _read_unlock)(o2); \
return r; \
} \
, ) \
\
M_IF_METHOD(CMP, oplist)( \
fattr int M_F(name, _cmp)(const shared_t *o1, const shared_t *o2) \
{ \
M_ASSERT(o1 != NULL && o2 != NULL); \
int neg = 1; \
/* See comment above on _write_read_lock */ \
if (o1 > o2) { \
M_SWAP(const shared_t *, o1, o2); \
/* inverse input, so inverse result too */ \
neg = -1; \
} \
M_F(name, _read_lock)(o1); \
M_F(name, _read_lock)(o2); \
/* CMP cannot throw exception */ \
int r = M_CALL_CMP(oplist, o1->data, o2->data); \
M_F(name, _read_unlock)(o1); \
M_F(name, _read_unlock)(o2); \
return r * neg; \
} \
, ) \
\
M_IF_METHOD(HASH, oplist)( \
fattr size_t M_F(name, _hash)(const shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _read_lock)(out); \
/* HASH cannot throw exception */ \
size_t r = M_CALL_HASH(oplist, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
, ) \
/* Define the arithmetic operators */
#define M_SHARED_PTR_DECL_ARITH(name, shared_t, oplist) \
M_IF_METHOD(ADD, oplist)( extern void M_F(name, _add)(shared_t *, const shared_t *); , ) \
M_IF_METHOD(SUB, oplist)( extern void M_F(name, _sub)(shared_t *, const shared_t *); , ) \
M_IF_METHOD(MUL, oplist)( extern void M_F(name, _mul)(shared_t *, const shared_t *); , ) \
M_IF_METHOD(DIV, oplist)( extern void M_F(name, _div)(shared_t *, const shared_t *); , ) \
M_IF_METHOD(SPLICE, oplist)( extern void M_F(name, _splice)(shared_t *, shared_t *); , ) \
#define M_SHARED_PTR_DEF_ARITH(name, shared_t, type, oplist, fattr) \
M_IF_METHOD(ADD, oplist)( \
fattr void M_F(name, _add)(shared_t *out, const shared_t *src) \
{ \
M_ASSERT(out != NULL && src != NULL); \
M_F(name, _write_read_lock)(out, src); \
M_ON_EXCEPTION( M_F(name, _write_read_unlock)(out, src) ) \
M_CALL_ADD(oplist, out->data, src->data); \
M_F(name, _write_read_unlock)(out, src); \
} \
, ) \
\
M_IF_METHOD(SUB, oplist)( \
fattr void M_F(name, _sub)(shared_t *out, const shared_t *src) \
{ \
M_ASSERT(out != NULL && src != NULL); \
M_F(name, _write_read_lock)(out, src); \
M_ON_EXCEPTION( M_F(name, _write_read_unlock)(out, src) ) \
M_CALL_SUB(oplist, out->data, src->data); \
M_F(name, _write_read_unlock)(out, src); \
} \
, ) \
\
M_IF_METHOD(MUL, oplist)( \
fattr void M_F(name, _mul)(shared_t *out, const shared_t *src) \
{ \
M_ASSERT(out != NULL && src != NULL); \
M_F(name, _write_read_lock)(out, src); \
M_ON_EXCEPTION( M_F(name, _write_read_unlock)(out, src) ) \
M_CALL_MUL(oplist, out->data, src->data); \
M_F(name, _write_read_unlock)(out, src); \
} \
, ) \
\
M_IF_METHOD(DIV, oplist)( \
fattr void M_F(name, _div)(shared_t *out, const shared_t *src) \
{ \
M_ASSERT(out != NULL && src != NULL); \
M_F(name, _write_read_lock)(out, src); \
M_ON_EXCEPTION( M_F(name, _write_read_unlock)(out, src) ) \
M_CALL_DIV(oplist, out->data, src->data); \
M_F(name, _write_read_unlock)(out, src); \
} \
, ) \
\
M_IF_METHOD(SPLICE, oplist)( \
fattr void M_F(name, _splice)(shared_t *out, shared_t *src) \
{ \
M_ASSERT(out != NULL && src != NULL); \
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_ON_EXCEPTION( M_F(name, _write_unlock)(out), M_F(name, _write_unlock)(src) ) \
M_CALL_SPLICE(oplist, out->data, src->data); \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
M_F(name, _write_unlock)(src); \
} \
, ) \
/* Define the key based operators */
#define M_SHARED_PTR_DECL_KEY(name, shared_t, oplist, key_type, value_type) \
M_IF_METHOD(GET_KEY, oplist)( extern bool M_F(name, _get)(value_type *, const shared_t *, key_type const); , ) \
M_IF_METHOD(SAFE_GET_KEY, oplist)( extern void M_F(name, _safe_get)(value_type *, shared_t *, key_type const); , ) \
M_IF_METHOD(SET_KEY, oplist)( extern void M_F(name, _set_at)(shared_t *, key_type const, value_type const); , ) \
M_IF_METHOD(ERASE_KEY, oplist)( extern void M_F(name, _erase_key)(shared_t *, key_type const); , )
#define M_SHARED_PTR_DEF_KEY(name, shared_t, type, oplist, fattr, key_type, key_oplist, value_type, value_oplist) \
M_IF_METHOD(GET_KEY, oplist)( \
fattr bool M_F(name, _get)(value_type *value, const shared_t *out, key_type const key) \
{ \
M_ASSERT (out != NULL); \
M_ASSERT (value != NULL); \
M_F(name, _read_lock)(out); \
/* No exception */ \
value_type *p = M_CALL_GET_KEY(oplist, out->data, key); \
if (p != NULL) { \
M_CALL_SET(value_oplist, *value, *p); \
} \
M_F(name, _read_unlock)(out); \
return p != NULL; \
} \
, ) \
/* FIXME: _try_get and _get is blocking? */ \
M_IF_METHOD(SAFE_GET_KEY, oplist)( \
fattr void M_F(name, _safe_get)(value_type *value, shared_t *out, key_type const key) \
{ \
M_ASSERT (out != NULL); \
M_ASSERT (value != NULL); \
M_F(name, _write_lock)(out); \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) { \
value_type *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \
M_ASSERT (p != NULL); \
M_CALL_SET(value_oplist, *value, *p); \
} \
/* FIXME: Signal?*/ \
M_F(name, _write_unlock)(out); \
} \
, ) \
\
M_IF_METHOD(SET_KEY, oplist)( \
fattr void M_F(name, _set_at)(shared_t *out, key_type const key, value_type const value) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _write_lock)(out); \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_SET_KEY(oplist, out->data, key, value); \
/* FIXME: Signal?*/ \
M_F(name, _write_unlock)(out); \
} \
, ) \
\
M_IF_METHOD(ERASE_KEY, oplist)( \
fattr bool M_F(name, _erase)(shared_t *out, key_type const key) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _write_lock)(out); \
/* No exception */ \
bool r = M_CALL_ERASE_KEY(oplist, out->data, key); \
/* FIXME: Signal?*/ \
M_F(name, _write_unlock)(out); \
return r; \
} \
, ) \
/* Define the push based operators */
#define M_SHARED_PTR_DECL_PUSH(name, shared_t, oplist, sub_type) \
M_IF_METHOD(PUSH, oplist)( extern void M_F(name, _push)(shared_t *, sub_type const); , ) \
M_IF_METHOD(PUSH_MOVE, oplist)( extern void M_F(name, _push_move)(shared_t *, sub_type *); , ) \
M_IF_METHOD(PUSH, oplist)( extern bool M_F(name, _try_push)(shared_t *, sub_type const); , ) \
M_IF_METHOD(PUSH_MOVE, oplist)( extern bool M_F(name, _try_push_move)(shared_t *, sub_type *); , ) \
M_EMPLACE_QUEUE_DEF(name, shared_t, M_F(name, _emplace), M_GET_OPLIST oplist, M_SHARED_PTR_DECL_EMPLACE) \
M_EMPLACE_QUEUE_DEF(name, shared_t, M_F(name, _emplace), M_GET_OPLIST oplist, M_SHARED_PTR_DECL_TRY_EMPLACE)
#define M_SHARED_PTR_DECL_EMPLACE(name, shared_t, function_name, oplist, init_func, exp_emplace_type) \
extern void function_name(shared_t*, M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type));
#define M_SHARED_PTR_DECL_TRY_EMPLACE(name, shared_t, function_name, oplist, init_func, exp_emplace_type) \
extern bool function_name(shared_t*, M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type));
#define M_SHARED_PTR_DEF_PUSH(name, shared_t, type, oplist, fattr, sub_type, sub_oplist) \
M_IF_METHOD(PUSH, oplist)( \
fattr void M_F(name, _push)(shared_t *out, sub_type const value) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _write_lock)(out); \
while (true) { \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_PUSH(oplist, out->data, value); \
M_F(name, _write_signal)(out); \
break; \
} \
M_F(name, _write_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
} \
\
fattr bool M_F(name, _try_push)(shared_t *out, sub_type const value) \
{ \
M_ASSERT(out != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_PUSH(oplist, out->data, value); \
M_F(name, _write_signal)(out); \
ret = true; \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
, ) \
M_IF_METHOD(PUSH_MOVE, oplist)( \
fattr void M_F(name, _push_move)(shared_t *out, sub_type *value) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _write_lock)(out); \
while (true) { \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_PUSH_MOVE(oplist, out->data, value); \
M_F(name, _write_signal)(out); \
break; \
} \
M_F(name, _write_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
} \
\
fattr bool M_F(name, _try_push_move)(shared_t *out, sub_type *value) \
{ \
M_ASSERT(out != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_PUSH_MOVE(oplist, out->data, value); \
M_F(name, _write_signal)(out); \
ret = true; \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
M_EMPLACE_QUEUE_DEF( (name, fattr, type), shared_t, M_F(name, _emplace), oplist, M_SHARED_PTR_DEF_EMPLACE) \
M_EMPLACE_QUEUE_DEF( (name, fattr, type), shared_t, M_F(name, _try_emplace), oplist, M_SHARED_PTR_DEF_TRY_EMPLACE) \
, ) \
#define M_SHARED_PTR_DEF_EMPLACE(name_attr, shared_t, function_name, oplist, init_func, exp_emplace_type) \
M_TRIPLE_2 name_attr void function_name(shared_t *out, M_EMPLACE_LIST_TYPE_VAR_ALTER(a, exp_emplace_type)) \
{ \
M_ASSERT(out != NULL); \
M_F(M_TRIPLE_1 name, _write_lock)(out); \
while (true) { \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(M_TRIPLE_1 name, _write_unlock)(out) ) { \
M_TRIPLE_3 name tmp; \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, tmp, exp_emplace_type); \
M_CALL_PUSH_MOVE(oplist, out->data, &tmp); \
M_F(name, _write_signal)(out); \
} \
break; \
} \
M_F(M_TRIPLE_1 name, _write_wait)(out); \
} \
M_F(M_TRIPLE_1 name, _write_unlock)(out); \
} \
#define M_SHARED_PTR_DEF_TRY_EMPLACE(name_attr, shared_t, function_name, oplist, init_func, exp_emplace_type) \
M_TRIPLE_2 name_attr bool function_name(shared_t *out, M_EMPLACE_LIST_TYPE_VAR_ALTER(a, exp_emplace_type)) \
{ \
M_ASSERT(out != NULL); \
bool ret = false; \
M_F(M_TRIPLE_1 name, _write_lock)(out); \
if (M_CALL_FULL_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(M_TRIPLE_1 name, _write_unlock)(out) ) { \
M_TRIPLE_3 name tmp; \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, tmp, exp_emplace_type); \
M_CALL_PUSH_MOVE(oplist, out->data, &tmp); \
M_F(name, _write_signal)(out); \
ret = true; \
} \
break; \
} \
M_F(M_TRIPLE_1 name, _write_unlock)(out); \
return ret; \
} \
/* Define the pop based operators */
#define M_SHARED_PTR_DECL_POP(name, shared_t, oplist, sub_type) \
M_IF_METHOD(POP, oplist)( extern void M_F(name, _pop)(sub_type *const, shared_t *); , ) \
M_IF_METHOD(POP_MOVE, oplist)( extern void M_F(name, _pop_move)(sub_type *, shared_t *); , ) \
M_IF_METHOD(POP, oplist)( extern bool M_F(name, _try_pop)(sub_type*, shared_t *); , ) \
M_IF_METHOD(POP_MOVE, oplist)( extern bool M_F(name, _try_pop_move)(sub_type *, shared_t *); , )
#define M_SHARED_PTR_DEF_POP(name, shared_t, type, oplist, fattr, sub_type, sub_oplist) \
M_IF_METHOD(POP, oplist)( \
fattr void M_F(name, _pop)(sub_type *value, shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _write_lock)(out); \
while (true) { \
if (M_CALL_EMPTY_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_POP(oplist, value, out->data); \
M_F(name, _free_signal)(out); \
break; \
} \
M_F(name, _read_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
} \
\
fattr bool M_F(name, _try_pop)(sub_type *value, shared_t *out) \
{ \
M_ASSERT(out != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
if (M_CALL_EMPTY_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_POP(oplist, value, out->data); \
M_F(name, _free_signal)(out); \
ret = true; \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
, ) \
M_IF_METHOD(POP_MOVE, oplist)( \
fattr void M_F(name, _pop_move)(sub_type *value, shared_t *out) \
{ \
M_ASSERT(out != NULL); \
M_F(name, _write_lock)(out); \
while (true) { \
if (M_CALL_EMPTY_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_POP_MOVE(oplist, value, out->data); \
M_F(name, _free_signal)(out); \
break; \
} \
M_F(name, _read_wait)(out); \
} \
M_F(name, _write_unlock)(out); \
} \
\
fattr bool M_F(name, _try_pop_move)(sub_type *value, shared_t *out) \
{ \
M_ASSERT(out != NULL); \
bool ret = false; \
M_F(name, _write_lock)(out); \
if (M_CALL_EMPTY_P(oplist, out->data) == false) { \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
M_CALL_PUSH_MOVE(oplist, out->data, value); \
M_F(name, _free_signal)(out); \
ret = true; \
} \
M_F(name, _write_unlock)(out); \
return ret; \
} \
, ) \
/* Define the iterator based operators
We cannot provide directly the iterator due to concurency API,
so we produce an encapsulation of the major usage of them */
#define M_SHARED_PTR_DECL_IT(name, shared_t, oplist, sub_type) \
M_IF_METHOD2(IT_FIRST, IT_REF, oplist)( extern int M_F(name, _apply)(shared_t *, int (*callback)(void *, sub_type*), void*); , ) \
M_IF_METHOD2(IT_FIRST, IT_CREF, oplist)( extern int M_F(name, _for_each)(const shared_t *, int (*callback)(void *, const sub_type*), void*); , ) \
M_IF_METHOD2(IT_LAST, IT_REF, oplist)( extern int M_F(name, _r_apply)(shared_t *, int (*callback)(void *, sub_type*), void*); , ) \
M_IF_METHOD2(IT_LAST, IT_CREF, oplist)( extern int M_F(name, _r_for_each)(const shared_t *, int (*callback)(void *, const sub_type*), void*); , ) \
#define M_SHARED_PTR_DEF_IT(name, shared_t, type, oplist, fattr, sub_type, sub_oplist) \
M_IF_METHOD2(IT_FIRST, IT_REF, oplist)( \
fattr int M_F(name, _apply)(shared_t *out, int (*callback)(void *, sub_type*), void*data) \
{ \
M_ASSERT(out != NULL); \
M_ASSERT(callback != NULL); \
int ret = 0; \
M_F(name, _write_lock)(out); \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) { \
M_GET_IT_TYPE oplist it; \
for(M_CALL_IT_FIRST(oplist, it, out->data); !M_CALL_IT_END_P(oplist, it); M_CALL_IT_NEXT(oplist, it)) { \
ret = callback(data, M_CALL_IT_REF(oplist, it)); \
if (ret) \
break; \
} \
} \
/* There is no NEW data in the container: no signal to do */ \
M_F(name, _write_unlock)(out); \
return ret; \
} \
, ) \
M_IF_METHOD2(IT_FIRST, IT_CREF, oplist)( \
fattr int M_F(name, _for_each)(const shared_t *out, int (*callback)(void *, const sub_type*), void*data) \
{ \
M_ASSERT(out != NULL); \
M_ASSERT(callback != NULL); \
int ret = 0; \
M_F(name, _read_lock)(out); \
M_ON_EXCEPTION( M_F(name, _read_unlock)(out) ) { \
M_GET_IT_TYPE oplist it; \
for(M_CALL_IT_FIRST(oplist, it, out->data); !M_CALL_IT_END_P(oplist, it); M_CALL_IT_NEXT(oplist, it)) { \
ret = callback(data, M_CALL_IT_CREF(oplist, it)); \
if (ret) \
break; \
} \
} \
M_F(name, _read_unlock)(out); \
return ret; \
} \
, ) \
M_IF_METHOD2(IT_LAST, IT_REF, oplist)( \
fattr int M_F(name, _r_apply)(shared_t *out, int (*callback)(void *, sub_type*), void*data) \
{ \
M_ASSERT(out != NULL); \
M_ASSERT(callback != NULL); \
int ret = 0; \
M_F(name, _write_lock)(out); \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) { \
M_GET_IT_TYPE oplist it; \
for(M_CALL_IT_LAST(oplist, it, out->data); !M_CALL_IT_END_P(oplist, it); M_CALL_IT_PREVIOUS(oplist, it)) { \
ret = callback(data, M_CALL_IT_REF(oplist, it)); \
if (ret) \
break; \
} \
} \
/* There is no NEW data in the container: no signal to do */ \
M_F(name, _write_unlock)(out); \
return ret; \
} \
, ) \
M_IF_METHOD2(IT_LAST, IT_CREF, oplist)( \
fattr int M_F(name, _r_for_each)(const shared_t *out, int (*callback)(void *, const sub_type*), void*data) \
{ \
M_ASSERT(out != NULL); \
M_ASSERT(callback != NULL); \
int ret = 0; \
M_F(name, _read_lock)(out); \
M_ON_EXCEPTION( M_F(name, _read_unlock)(out) ) { \
M_GET_IT_TYPE oplist it; \
for(M_CALL_IT_LAST(oplist, it, out->data); !M_CALL_IT_END_P(oplist, it); M_CALL_IT_PREVIOUS(oplist, it)) { \
ret = callback(data, M_CALL_IT_CREF(oplist, it)); \
if (ret) \
break; \
} \
} \
M_F(name, _read_unlock)(out); \
return ret; \
} \
, ) \
/* Define the I/O based operators */
#define M_SHARED_PTR_DECL_IO(name, shared_t, oplist) \
M_IF_METHOD(OUT_STR, oplist)( extern void M_F(name, _out_str)(FILE *, const shared_t *); , ) \
M_IF_METHOD(IN_STR, oplist)( extern bool M_F(name, _in_str)(shared_t *, FILE *); , ) \
M_IF_METHOD(GET_STR, oplist)( extern void M_F(name, _get_str)(string_t, const shared_t *, bool); , ) \
M_IF_METHOD(PARSE_STR, oplist)( extern bool M_F(name, _parse_str)(shared_t *, const char *, const char **); , ) \
M_IF_METHOD(OUT_SERIAL, oplist)( extern m_serial_return_code_t M_F(name, _out_serial)(m_serial_write_t, const shared_t *); , ) \
M_IF_METHOD(IN_SERIAL, oplist)( extern m_serial_return_code_t M_F(name, _in_serial)(shared_t *, m_serial_read_t); , ) \
#define M_SHARED_PTR_DEF_IO(name, shared_t, type, oplist, fattr) \
M_IF_METHOD(OUT_STR, oplist)( \
fattr void M_F(name, _out_str)(FILE *file, const shared_t *out) \
{ \
M_ASSERT (out != NULL && file != NULL); \
M_F(name, _read_lock)(out); \
/* No exception */ \
M_CALL_OUT_STR(oplist, file, out->data); \
M_F(name, _read_unlock)(out); \
} \
, ) \
M_IF_METHOD(IN_STR, oplist)( \
fattr bool M_F(name, _in_str)(shared_t *out, FILE *file) \
{ \
M_ASSERT (out != NULL && file != NULL); \
M_F(name, _write_lock)(out); \
bool r m_volatile = false; \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
r = M_CALL_IN_STR(oplist, out->data, file); \
/* even if r is false, we signal the data (no functional impact)*/ \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return r; \
} \
, ) \
M_IF_METHOD(GET_STR, oplist)( \
fattr void M_F(name, _get_str)(string_t str, const shared_t *out, bool append) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _read_lock)(out); \
/* No exception */ \
M_CALL_GET_STR(oplist, str, out->data, append); \
M_F(name, _read_unlock)(out); \
} \
, ) \
M_IF_METHOD(PARSE_STR, oplist)( \
fattr bool M_F(name, _parse_str)(shared_t *out, const char str[], const char **endp) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _write_lock)(out); \
bool m_volatile r = false; \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
r = M_CALL_PARSE_STR(oplist, out->data, str, endp); \
/* even if r is false, we signal the data (no functional impact)*/ \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return r; \
} \
, ) \
M_IF_METHOD(OUT_SERIAL, oplist)( \
fattr m_serial_return_code_t M_F(name, _out_serial)(m_serial_write_t serial, const shared_t *out) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _read_lock)(out); \
/* No exception */ \
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, serial, out->data); \
M_F(name, _read_unlock)(out); \
return r; \
} \
, ) \
M_IF_METHOD(IN_SERIAL, oplist)( \
fattr m_serial_return_code_t M_F(name, _in_serial)(shared_t *out, m_serial_read_t serial) \
{ \
M_ASSERT (out != NULL); \
M_F(name, _write_lock)(out); \
m_serial_return_code_t m_volatile r = M_SERIAL_FAIL; \
M_ON_EXCEPTION( M_F(name, _write_unlock)(out) ) \
r = M_CALL_IN_SERIAL(oplist, out->data, serial); \
/* even if r is false, we signal the data (no functional impact)*/ \
M_F(name, _write_signal)(out); \
M_F(name, _write_unlock)(out); \
return r; \
} \
, ) \
#if M_USE_SMALL_NAME
#define SHARED_WEAK_PTR_DECL M_SHARED_WEAK_PTR_DECL
#define SHARED_WEAK_PTR_DECL_AS M_SHARED_WEAK_PTR_DECL_AS
#define SHARED_PTR_DECL M_SHARED_PTR_DECL
#define SHARED_PTR_DECL_AS M_SHARED_PTR_DECL_AS
#define SHARED_WEAK_PTR_DEF_EXTERN M_SHARED_WEAK_PTR_DEF_EXTERN
#define SHARED_WEAK_PTR_DEF_EXTERN_AS M_SHARED_WEAK_PTR_DEF_EXTERN_AS
#define SHARED_PTR_DEF_EXTERN M_SHARED_PTR_DEF_EXTERN
#define SHARED_PTR_DEF_EXTERN_AS M_SHARED_PTR_DEF_EXTERN_AS
#define SHARED_WEAK_PTR_DEF M_SHARED_WEAK_PTR_DEF
#define SHARED_WEAK_PTR_DEF_AS M_SHARED_WEAK_PTR_DEF_AS
#define SHARED_PTR_DEF M_SHARED_PTR_DEF
#define SHARED_PTR_DEF_AS M_SHARED_PTR_DEF_AS
#endif
#endif