This make the name shorter, give the user full control over the prefix, give the user full power over the name, allow more flexibility over the kind of container (allowing switch quickly), and uniformize with variant_, tuple_ & algo_. This is better than the current situation and all containers will switch. ==> Break API compatibility: To fix this quickly, just add shared_ before the name into the macro SHARED_PTR_DEF & SHARED_OPLIST. This will generate the same name than before.
252 lines
12 KiB
C
252 lines
12 KiB
C
/*
|
|
* M*LIB - SHARED PTR Module
|
|
*
|
|
* Copyright (c) 2017, 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 __M_SHARED_PTR__H
|
|
#define __M_SHARED_PTR__H
|
|
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#include "m-core.h"
|
|
#include "m-atomic.h"
|
|
|
|
/* Define the oplist of a shared pointer.
|
|
USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
|
|
#define SHARED_PTR_OPLIST(...) \
|
|
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
(SHAREDI_PTR_OPLIST(__VA_ARGS__, M_DEFAULT_OPLIST ), \
|
|
SHAREDI_PTR_OPLIST(__VA_ARGS__ ))
|
|
|
|
/* Define shared pointer and its function.
|
|
USAGE: SHARED_PTR_DEF(name[, oplist]) */
|
|
#define SHARED_PTR_DEF(name, ...) \
|
|
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
(SHAREDI_PTR_DEF2(name, __VA_ARGS__, M_DEFAULT_OPLIST ), \
|
|
SHAREDI_PTR_DEF2(name, __VA_ARGS__ ))
|
|
|
|
/********************************** INTERNAL ************************************/
|
|
|
|
#define SHAREDI_PTR_OPLIST(name, oplist) ( \
|
|
INIT(M_C(name, _init)), \
|
|
CLEAR(M_C(name, _clear)), \
|
|
INIT_SET(M_C(name, _init_set)), \
|
|
SET(M_C(name, _set)) \
|
|
INIT_MOVE(M_C(name, _init_move)), \
|
|
CLEAN(M_C(name, _clean)), \
|
|
MOVE(M_C(name, _move)), \
|
|
SWAP(M_C(name, _swap)) \
|
|
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
|
|
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
|
|
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
|
)
|
|
|
|
#define SHAREDI_PTR_DEF2(name, type, oplist) \
|
|
\
|
|
typedef struct M_C(name, _s){ \
|
|
type *data; /* Pointer to the data */ \
|
|
atomic_int cpt; /* Counter of how many points to the data */ \
|
|
bool combineAlloc; /* Does the data and the ptr share the slot? */ \
|
|
} *M_C(name, _t)[1]; \
|
|
\
|
|
typedef struct M_C(name, combine_s) { \
|
|
type data; \
|
|
struct M_C(name, _s) ptr; \
|
|
} M_C(name, combine_t)[1]; \
|
|
\
|
|
static inline void \
|
|
M_C(name, _init)(M_C(name, _t) shared) \
|
|
{ \
|
|
*shared = NULL; \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _init2)(M_C(name, _t) shared, type *data) \
|
|
{ \
|
|
struct M_C(name, _s) *ptr; \
|
|
if (data == NULL) { \
|
|
*shared = NULL; \
|
|
return; \
|
|
} \
|
|
ptr = M_GET_NEW oplist (struct M_C(name, _s)); \
|
|
if (ptr == NULL) { \
|
|
M_MEMORY_FULL(sizeof(struct M_C(name, _s))); \
|
|
return; \
|
|
} \
|
|
ptr->data = data; \
|
|
atomic_init (&ptr->cpt, 1); \
|
|
ptr->combineAlloc = false; \
|
|
*shared = ptr; \
|
|
SHAREDI_CONTRACT(shared); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _init_new)(M_C(name, _t) shared) \
|
|
{ \
|
|
/* NOTE: Alloc 1 struct with both structures. */ \
|
|
struct M_C(name, combine_s) *p = \
|
|
M_GET_NEW oplist (struct M_C(name, combine_s)); \
|
|
if (p == NULL) { \
|
|
M_MEMORY_FULL(sizeof(struct M_C(name, combine_s))); \
|
|
return; \
|
|
} \
|
|
struct M_C(name, _s) *ptr = &p->ptr; \
|
|
type *data = &p->data; \
|
|
M_GET_INIT oplist(*data); \
|
|
ptr->data = data; \
|
|
atomic_init (&ptr->cpt, 1); \
|
|
ptr->combineAlloc = true; \
|
|
*shared = ptr; \
|
|
SHAREDI_CONTRACT(shared); \
|
|
} \
|
|
\
|
|
static inline bool \
|
|
M_C(name, _NULL_p)(const M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(shared); \
|
|
return *shared == NULL; \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _init_set)(M_C(name, _t) dest, \
|
|
const M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(shared); \
|
|
assert (dest != shared); \
|
|
*dest = *shared; \
|
|
if (*dest != NULL) \
|
|
atomic_fetch_add(&((*dest)->cpt), 1); \
|
|
SHAREDI_CONTRACT(dest); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _clear)(M_C(name, _t) dest) \
|
|
{ \
|
|
SHAREDI_CONTRACT(dest); \
|
|
if (*dest != NULL) { \
|
|
if (atomic_fetch_sub(&((*dest)->cpt), 1) == 1) { \
|
|
bool combineAlloc = (*dest)->combineAlloc; \
|
|
/* Note: if combineAlloc is true, the address of the slot \
|
|
combining both data & ptr is the same as the address of the \
|
|
first element, aka data itself. Static analyzer tools don't \
|
|
seem to detect this and report error. */ \
|
|
M_GET_CLEAR oplist (*(*dest)->data); \
|
|
M_GET_DEL oplist ((*dest)->data); \
|
|
if (combineAlloc == false) \
|
|
M_GET_DEL oplist (*dest); \
|
|
} \
|
|
*dest = NULL; \
|
|
} \
|
|
SHAREDI_CONTRACT(dest); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _clean)(M_C(name, _t) dest) \
|
|
{ \
|
|
/* NOTE: Clear will also set dest to NULL */ \
|
|
M_C(name, _clear)(dest); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _set)(M_C(name, _t) dest, \
|
|
const M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(dest); \
|
|
SHAREDI_CONTRACT(shared); \
|
|
M_C(name, _clear)(dest); \
|
|
M_C(name, _init_set)(dest, shared); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _init_move)(M_C(name, _t) dest, \
|
|
M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(shared); \
|
|
assert (dest != NULL && dest != shared); \
|
|
*dest = *shared; \
|
|
*shared = NULL; \
|
|
SHAREDI_CONTRACT(dest); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _move)(M_C(name, _t) dest, \
|
|
M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(dest); \
|
|
SHAREDI_CONTRACT(shared); \
|
|
assert (dest != shared); \
|
|
M_C(name, _clear)(dest); \
|
|
M_C(name, _init_move)(dest, shared); \
|
|
} \
|
|
\
|
|
static inline void \
|
|
M_C(name, _swap)(M_C(name, _t) p1, \
|
|
M_C(name, _t) p2) \
|
|
{ \
|
|
SHAREDI_CONTRACT(p1); \
|
|
SHAREDI_CONTRACT(p2); \
|
|
M_SWAP (struct M_C(name, _s)*, *p1, *p2); \
|
|
SHAREDI_CONTRACT(p1); \
|
|
SHAREDI_CONTRACT(p2); \
|
|
} \
|
|
\
|
|
static inline bool \
|
|
M_C(name, _equal_p)(const M_C(name, _t) p1, \
|
|
const M_C(name, _t) p2) \
|
|
{ \
|
|
SHAREDI_CONTRACT(p1); \
|
|
SHAREDI_CONTRACT(p2); \
|
|
return *p1 == *p2; \
|
|
} \
|
|
\
|
|
static inline const type * \
|
|
M_C(name, _cref)(const M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(shared); \
|
|
assert(*shared != NULL); \
|
|
type *data = (*shared)->data; \
|
|
assert (data != NULL); \
|
|
return M_CONST_CAST (type, data); \
|
|
} \
|
|
\
|
|
static inline type * \
|
|
M_C(name, _ref)(M_C(name, _t) shared) \
|
|
{ \
|
|
SHAREDI_CONTRACT(shared); \
|
|
assert(*shared != NULL); \
|
|
type *data = (*shared)->data; \
|
|
assert (data != NULL); \
|
|
return data; \
|
|
} \
|
|
|
|
/********************************** INTERNAL ************************************/
|
|
|
|
#define SHAREDI_CONTRACT(shared) do { \
|
|
assert(shared != NULL); \
|
|
assert(*shared == NULL || atomic_load (&(*shared)->cpt) >= 1); \
|
|
} while (0)
|
|
|
|
#endif
|