/* * 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) = M_ATOMIC_VAR_INIT(0) /* Value of the interface field for static intialization (Uses C89 designated element). */ #define M_ISHARED_PTR_STATIC_INIT(name, type) \ M_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