it is used to control the inline semantics. By combining it with optimize option "-fno-inline" for the internal M*LIB function (using _Pragma optimize) we can control easily if we want to use an inline version or call of an external function. Define new user API: * M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then. * M_USE_EXTERN_DEF : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too. If you use one option, you need to use the other one too for another translation unit.
256 lines
16 KiB
C
256 lines
16 KiB
C
/*
|
|
* M*LIB - INTRUSIVE SHARED PTR Module
|
|
*
|
|
* Copyright (c) 2017-2023, Patrick Pelissier
|
|
* All rights reserved.
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* + Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* + Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef MSTARLIB_I_SHARED_PTR_H
|
|
#define MSTARLIB_I_SHARED_PTR_H
|
|
|
|
#include "m-core.h"
|
|
#include "m-atomic.h"
|
|
|
|
M_BEGIN_PROTECTED_CODE
|
|
|
|
/* Define the oplist of a intrusive shared pointer.
|
|
USAGE: ISHARED_OPLIST(name [, oplist_of_the_type]) */
|
|
#define M_ISHARED_PTR_OPLIST(...) \
|
|
M_ISHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((__VA_ARGS__, M_BASIC_OPLIST), \
|
|
(__VA_ARGS__ )))
|
|
|
|
|
|
/* Interface to add to a structure to allow intrusive support.
|
|
name: name of the intrusive shared pointer.
|
|
type: name of the type of the structure (aka. struct test_s) - not used currently.
|
|
NOTE: There can be only one interface of this kind in a type! */
|
|
#define M_ISHARED_PTR_INTERFACE(name, type) \
|
|
atomic_int M_F(name, _cpt)
|
|
|
|
|
|
/* Value of the interface field for static intialization (Uses C99 designated element). */
|
|
#define M_ISHARED_PTR_STATIC_DESIGNATED_INIT(name, type) \
|
|
.M_F(name, _cpt) = ATOMIC_VAR_INIT(0)
|
|
|
|
/* Value of the interface field for static intialization (Uses C89 designated element). */
|
|
#define M_ISHARED_PTR_STATIC_INIT(name, type) \
|
|
ATOMIC_VAR_INIT(0)
|
|
|
|
|
|
/* Define the intrusive shared pointer type and its M_INLINE functions.
|
|
USAGE: ISHARED_PTR_DEF(name, type, [, oplist]) */
|
|
#define M_ISHARED_PTR_DEF(name, ...) \
|
|
M_ISHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)
|
|
|
|
|
|
/* Define the intrusive shared pointer type and its M_INLINE functions
|
|
as the name name_t
|
|
USAGE: ISHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */
|
|
#define M_ISHARED_PTR_DEF_AS(name, name_t, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_ISHAR3D_PTR_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
|
|
|
|
|
|
/*****************************************************************************/
|
|
/******************************** INTERNAL ***********************************/
|
|
/*****************************************************************************/
|
|
|
|
// Deferred evaluation
|
|
#define M_ISHAR3D_PTR_OPLIST_P1(arg) M_ISHAR3D_PTR_OPLIST_P2 arg
|
|
|
|
/* Validation of the given oplist */
|
|
#define M_ISHAR3D_PTR_OPLIST_P2(name, oplist) \
|
|
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_OPLIST_P3, M_ISHAR3D_PTR_OPLIST_FAILURE)(name, oplist)
|
|
|
|
/* Prepare a clean compilation failure */
|
|
#define M_ISHAR3D_PTR_OPLIST_FAILURE(name, oplist) \
|
|
((M_LIB_ERROR(ARGUMENT_OF_ISHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
|
|
|
// Define the oplist
|
|
#define M_ISHAR3D_PTR_OPLIST_P3(name, oplist) ( \
|
|
INIT(M_INIT_DEFAULT), \
|
|
INIT_SET(API_4(M_F(name, _init_set))), \
|
|
SET(M_F(name, _set) M_IPTR), \
|
|
CLEAR(M_F(name, _clear)), \
|
|
RESET(M_F(name, _reset) M_IPTR), \
|
|
NAME(name), \
|
|
TYPE(M_F(name, _ct)), \
|
|
OPLIST(oplist), \
|
|
SUBTYPE(M_F(name, _subtype_ct)) \
|
|
)
|
|
|
|
|
|
/******************************** INTERNAL ***********************************/
|
|
|
|
// Deferred evaluatioin
|
|
#define M_ISHAR3D_PTR_DEF_P1(arg) M_ID( M_ISHAR3D_PTR_DEF_P2 arg )
|
|
|
|
/* Validate the oplist before going further */
|
|
#define M_ISHAR3D_PTR_DEF_P2(name, type, oplist, shared_t) \
|
|
M_IF_OPLIST(oplist)(M_ISHAR3D_PTR_DEF_P3, M_ISHAR3D_PTR_DEF_FAILURE)(name, type, oplist, shared_t)
|
|
|
|
/* Stop processing with a compilation failure */
|
|
#define M_ISHAR3D_PTR_DEF_FAILURE(name, type, oplist, shared_t) \
|
|
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ISHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)
|
|
|
|
#define M_ISHAR3D_PTR_DEF_P3(name, type, oplist, shared_t) \
|
|
M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
|
|
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
|
M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
|
|
|
|
/* Define the types */
|
|
#define M_ISHAR3D_PTR_DEF_TYPE(name, type, oplist, shared_t) \
|
|
\
|
|
/* The shared pointer is only a pointer to the type */ \
|
|
typedef type *shared_t; \
|
|
\
|
|
/* Define internal types for oplist */ \
|
|
typedef shared_t M_F(name, _ct); \
|
|
typedef type M_F(name, _subtype_ct); \
|
|
|
|
/* Define the core functions */
|
|
#define M_ISHAR3D_PTR_DEF_CORE(name, type, oplist, shared_t) \
|
|
\
|
|
M_INLINE shared_t \
|
|
M_F(name, _init)(type *ptr) \
|
|
{ \
|
|
/* Initialize the type referenced by the pointer */ \
|
|
if (M_LIKELY (ptr != NULL)) { \
|
|
atomic_init(&ptr->M_F(name, _cpt), 2); \
|
|
} \
|
|
return ptr; \
|
|
} \
|
|
\
|
|
M_INLINE shared_t \
|
|
M_F(name, _init_set)(shared_t shared) \
|
|
{ \
|
|
if (M_LIKELY (shared != NULL)) { \
|
|
int n = atomic_fetch_add(&(shared->M_F(name, _cpt)), 2); \
|
|
(void) n; \
|
|
} \
|
|
return shared; \
|
|
} \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_IF_DISABLED_METHOD(NEW, oplist) \
|
|
( \
|
|
/* This function is only for static object */ \
|
|
M_INLINE shared_t \
|
|
M_F(name, _init_once)(type *shared) \
|
|
{ \
|
|
if (M_LIKELY (shared != NULL)) { \
|
|
/* Pretty much like atomic_add, except the first one increment by 1, others by 2 */ \
|
|
int o = atomic_load(&(shared->M_F(name, _cpt))); \
|
|
int n; \
|
|
do { \
|
|
n = o + 1 + (o != 0); \
|
|
} while (!atomic_compare_exchange_strong(&(shared->M_F(name, _cpt)), &o, n)); \
|
|
if (o == 0) { \
|
|
/* Partial initialization: _cpt is odd */ \
|
|
/* Call the INIT function once */ \
|
|
M_CALL_INIT(oplist, *shared); \
|
|
/* Finish initialization: _cpt is even */ \
|
|
atomic_fetch_add(&(shared->M_F(name, _cpt)), 1); \
|
|
} else if ( (o&1) != 0) { \
|
|
/* Not fully initialized yet: wait for initialization */ \
|
|
m_core_backoff_ct bkoff; \
|
|
m_core_backoff_init(bkoff); \
|
|
/* Wait for _cpt to be _even */ \
|
|
while ((atomic_load(&(shared->M_F(name, _cpt)))&1) != 0 ) { \
|
|
m_core_backoff_wait(bkoff); \
|
|
} \
|
|
} \
|
|
M_ASSERT( (atomic_load(&(shared->M_F(name, _cpt)))&1) == 0); \
|
|
} \
|
|
return shared; \
|
|
} \
|
|
, \
|
|
/* This function is only for dynamic object */ \
|
|
M_INLINE shared_t \
|
|
M_F(name, _init_new)(void) \
|
|
{ \
|
|
type *ptr = M_CALL_NEW(oplist, type); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
|
|
M_MEMORY_FULL(sizeof(type)); \
|
|
return NULL; \
|
|
} \
|
|
M_CALL_INIT(oplist, *ptr); \
|
|
atomic_init (&ptr->M_F(name, _cpt), 2); \
|
|
return ptr; \
|
|
} \
|
|
/* End of NEW */) \
|
|
, /* End of INIT */) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _clear)(shared_t shared) \
|
|
{ \
|
|
if (shared != NULL) { \
|
|
if (atomic_fetch_sub(&(shared->M_F(name, _cpt)), 2) == 2) { \
|
|
M_CALL_CLEAR(oplist, *shared); \
|
|
M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \
|
|
} \
|
|
} \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _clear_ptr)(shared_t *shared) \
|
|
{ \
|
|
M_ASSERT(shared != NULL); \
|
|
M_F(name, _clear)(*shared); \
|
|
*shared = NULL; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _reset)(shared_t *shared) \
|
|
{ \
|
|
M_F(name, _clear)(*shared); \
|
|
*shared = NULL; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _set)(shared_t *ptr, shared_t shared) \
|
|
{ \
|
|
M_ASSERT (ptr != NULL); \
|
|
if (M_LIKELY (*ptr != shared)) { \
|
|
M_F(name, _clear)(*ptr); \
|
|
*ptr = M_F(name, _init_set)(shared); \
|
|
} \
|
|
} \
|
|
\
|
|
|
|
M_END_PROTECTED_CODE
|
|
|
|
/******************************** INTERNAL ***********************************/
|
|
|
|
#if M_USE_SMALL_NAME
|
|
#define ISHARED_PTR_OPLIST M_ISHARED_PTR_OPLIST
|
|
#define ISHARED_PTR_INTERFACE M_ISHARED_PTR_INTERFACE
|
|
#define ISHARED_PTR_STATIC_DESIGNATED_INIT M_ISHARED_PTR_STATIC_DESIGNATED_INIT
|
|
#define ISHARED_PTR_STATIC_INIT M_ISHARED_PTR_STATIC_INIT
|
|
#define ISHARED_PTR_DEF M_ISHARED_PTR_DEF
|
|
#define ISHARED_PTR_DEF_AS M_ISHARED_PTR_DEF_AS
|
|
#endif
|
|
|
|
#endif
|