From 34c990e2594c4e917b2978a8c8d4d13b4a860045 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 5 Dec 2020 18:14:22 +0100 Subject: [PATCH] Add function _init_once for intrusive shared pointer --- README.md | 28 +++++++++++++++++++++++++--- m-i-shared.h | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c4818a9b..3d707383 100644 --- a/README.md +++ b/README.md @@ -4019,12 +4019,22 @@ TODO: Document shared resource This header is for creating intrusive shared pointer. -#### ISHARED\_INTERFACE(name, type) +#### ISHARED\_PTR\_INTERFACE(name, type) Extend the definition of the structure of an object of type 'type' by adding the necessary interface to handle it as a shared pointer named 'name'. It shall be put within the structure definition of the object (See example). +#### ISHARED\_PTR\_STATIC\_INIT(name, type) + +Provide the static initialization value of an object defined with a +ISHARED\_PTR\_INTERFACE extra fields. It shall be used only for global +variables with the \_init\_once function. + +Usage: + + struct mystruct variable = { ISHARED_PTR_STATIC_INIT(ishared_double, struct mystruct) }; + #### ISHARED\_PTR\_DEF(name, type[, oplist]) Define the associated methods to handle the shared pointer named 'name' @@ -4060,6 +4070,7 @@ so that the CLEAR method doesn't try to free the objectslike this: (NEW(0), DEL(0)) +NEW & DEL operators shall be either both defined, or both disabled. Example (dynamic): @@ -4116,12 +4127,23 @@ This function is thread safe. Allocate a new object, initialize it and return an initialized shared pointer to it. -The used allocation function is the ALLOC operator. -In this case, it is assumed that the DEL operator has not been disabled. +The used allocation function is the NEW operator. This function is created only if the INIT method is defined in the oplist and if the NEW method has not been disabled in the oplist. +##### name\_t name\_init\_once(type *object) + +Initialize the new object 'object' and return an initialized shared pointer to it. +The INIT operator of 'object' is ensured to be called only once, +even if multiple threads try to initialize it at the same time. +Once the object is fully cleared, the initialization function may occur once again. + +object shall be a global variable initialized with the ISHARED_PTR_STATIC_INIT macro. + +This function is created only if the INIT method is defined in the oplist +and if the NEW method has been disabled in the oplist. + ##### void name\_clear(name\_t shared) Clear the shared pointer, releasing ownership of the object diff --git a/m-i-shared.h b/m-i-shared.h index 9208c75d..7f07af62 100644 --- a/m-i-shared.h +++ b/m-i-shared.h @@ -46,6 +46,11 @@ M_BEGIN_PROTECTED_CODE atomic_int M_C(name, _cpt) +/* Value of the interface field for static intialization. */ +#define ISHARED_PTR_STATIC_INIT(name, type) \ + .M_C(name, _cpt) = ATOMIC_VAR_INIT(0) + + /* Define the intrusive shared pointer type and its static inline functions. USAGE: ISHARED_PTR_DEF(name, type, [, oplist]) */ #define ISHARED_PTR_DEF(name, ...) \ @@ -122,7 +127,7 @@ M_BEGIN_PROTECTED_CODE { \ /* Initialize the type referenced by the pointer */ \ if (M_LIKELY (ptr != NULL)) { \ - atomic_init(&ptr->M_C(name, _cpt), 1); \ + atomic_init(&ptr->M_C(name, _cpt), 2); \ } \ return ptr; \ } \ @@ -131,7 +136,7 @@ M_BEGIN_PROTECTED_CODE M_C(name, _init_set)(shared_t shared) \ { \ if (M_LIKELY (shared != NULL)) { \ - int n = atomic_fetch_add(&(shared->M_C(name, _cpt)), 1); \ + int n = atomic_fetch_add(&(shared->M_C(name, _cpt)), 2); \ (void) n; \ } \ return shared; \ @@ -144,9 +149,41 @@ M_BEGIN_PROTECTED_CODE *ptr = M_C(name, _init_set)(shared); \ } \ \ + M_IF_METHOD(INIT, oplist)( \ M_IF_DISABLED_METHOD(NEW, oplist) \ - ( /* Nothing to do */, M_IF_METHOD(INIT, oplist) \ - ( \ + ( \ + /* This function is only for static object */ \ + static inline shared_t \ + M_C(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_C(name, _cpt))); \ + int n; \ + do { \ + n = o + 1 + (o != 0); \ + } while (!atomic_compare_exchange_strong(&(shared->M_C(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_C(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_C(name, _cpt)))&1) != 0 ) { \ + m_core_backoff_wait(bkoff); \ + } \ + } \ + M_ASSERT( (atomic_load(&(shared->M_C(name, _cpt)))&1) == 0); \ + } \ + return shared; \ + } \ + , \ + /* This function is only for dynamic object */ \ static inline shared_t \ M_C(name, _init_new)(void) \ { \ @@ -156,17 +193,17 @@ M_BEGIN_PROTECTED_CODE return NULL; \ } \ M_CALL_INIT(oplist, *ptr); \ - atomic_init (&ptr->M_C(name, _cpt), 1); \ + atomic_init (&ptr->M_C(name, _cpt), 2); \ return ptr; \ } \ - , /* End of INIT */) \ /* End of NEW */) \ + , /* End of INIT */) \ \ static inline void \ M_C(name, _clear)(shared_t shared) \ { \ if (shared != NULL) { \ - if (atomic_fetch_sub(&(shared->M_C(name, _cpt)), 1) == 1) { \ + if (atomic_fetch_sub(&(shared->M_C(name, _cpt)), 2) == 2) { \ M_CALL_CLEAR(oplist, *shared); \ M_IF_DISABLED_METHOD(DEL, oplist)(, M_CALL_DEL(oplist, shared);) \ } \