255 lines
7.6 KiB
C++
255 lines
7.6 KiB
C++
/* MIT License
|
|
*
|
|
* Copyright (c) 2025 Tyge Løvset
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
/* arc: atomic reference counted shared_ptr (new implementation)
|
|
*
|
|
* The difference between arc and arc2 is that arc only takes up one pointer,
|
|
* whereas arc2 uses two. arc cannot be constructed from an already allocated pointer,
|
|
* which arc2 may. To use arc2, specify the `(c_arc2)` option after the key type, e.g.:
|
|
* #define T MyArc, MyType, (c_arc2 | c_no_atomic)
|
|
*/
|
|
/*
|
|
#include <stc/cstr.h>
|
|
|
|
typedef struct { cstr name, last; } Person;
|
|
|
|
Person Person_make(const char* name, const char* last) {
|
|
return (Person){.name = cstr_from(name), .last = cstr_from(last)};
|
|
}
|
|
Person Person_clone(Person p) {
|
|
p.name = cstr_clone(p.name);
|
|
p.last = cstr_clone(p.last);
|
|
return p;
|
|
}
|
|
void Person_drop(Person* p) {
|
|
printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last));
|
|
cstr_drop(&p->name);
|
|
cstr_drop(&p->last);
|
|
}
|
|
|
|
#define T ArcPers, Person, (c_keyclass) // clone, drop, cmp, hash
|
|
#include <stc/arc.h>
|
|
|
|
int main(void) {
|
|
ArcPers p = ArcPers_from(Person_make("John", "Smiths"));
|
|
ArcPers q = ArcPers_clone(p); // share the pointer
|
|
|
|
printf("%s %s. uses: %ld\n", cstr_str(&q.get->name), cstr_str(&q.get->last), ArcPers_use_count(q));
|
|
c_drop(ArcPers, &p, &q);
|
|
}
|
|
*/
|
|
#include "priv/linkage.h"
|
|
#include "types.h"
|
|
|
|
#ifndef STC_ARC_H_INCLUDED
|
|
#define STC_ARC_H_INCLUDED
|
|
#include "common.h"
|
|
#include <stdlib.h>
|
|
|
|
#if defined __GNUC__ || defined __clang__ || defined _MSC_VER || defined i_no_atomic
|
|
typedef long catomic_long;
|
|
#else // try with C11
|
|
typedef _Atomic(long) catomic_long;
|
|
#endif
|
|
#if defined _MSC_VER
|
|
#include <intrin.h>
|
|
#define c_atomic_inc(v) (void)_InterlockedIncrement(v)
|
|
#define c_atomic_dec_and_test(v) !_InterlockedDecrement(v)
|
|
#elif defined __GNUC__ || defined __clang__
|
|
#define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST)
|
|
#define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST)
|
|
#else // try with C11
|
|
#include <stdatomic.h>
|
|
#define c_atomic_inc(v) (void)atomic_fetch_add(v, 1)
|
|
#define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1)
|
|
#endif
|
|
#endif // STC_ARC_H_INCLUDED
|
|
|
|
#ifndef _i_prefix
|
|
#define _i_prefix arc_
|
|
#endif
|
|
#define _i_is_arc
|
|
#include "priv/template.h"
|
|
typedef i_keyraw _m_raw;
|
|
|
|
#if c_OPTION(c_no_atomic)
|
|
#define i_no_atomic
|
|
#endif
|
|
#if !defined i_no_atomic
|
|
#define _i_atomic_inc(v) c_atomic_inc(v)
|
|
#define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v)
|
|
#else
|
|
#define _i_atomic_inc(v) (void)(++*(v))
|
|
#define _i_atomic_dec_and_test(v) !(--*(v))
|
|
#endif
|
|
|
|
#if c_OPTION(c_arc2)
|
|
#define i_arc2
|
|
#endif
|
|
#if !(defined i_arc2 || defined STC_USE_ARC2)
|
|
// ------------ Arc1 size of one pointer (union) -------------
|
|
|
|
#ifndef i_declared
|
|
_c_DEFTYPES(declare_arc, Self, i_key);
|
|
#endif
|
|
struct _c_MEMB(_ctrl) {
|
|
_m_value value;
|
|
catomic_long counter;
|
|
};
|
|
|
|
// c++: std::make_shared<_m_value>(val)
|
|
STC_INLINE Self _c_MEMB(_make)(_m_value val) {
|
|
Self arc = {.ctrl=_i_new_n(_c_MEMB(_ctrl), 1)};
|
|
arc.ctrl->value = val;
|
|
arc.ctrl->counter = 1;
|
|
return arc;
|
|
}
|
|
|
|
STC_INLINE Self _c_MEMB(_toarc)(_m_value* arc_raw)
|
|
{ Self arc = {.ctrl=(_c_MEMB(_ctrl) *)arc_raw}; return arc; }
|
|
|
|
// destructor
|
|
STC_INLINE void _c_MEMB(_drop)(const Self* self) {
|
|
if (self->ctrl && _i_atomic_dec_and_test(&self->ctrl->counter)) {
|
|
i_keydrop(self->get);
|
|
i_free(self->ctrl, c_sizeof *self->ctrl);
|
|
}
|
|
}
|
|
|
|
#else // ------------ Arc2 size of two pointers -------------
|
|
|
|
#ifndef i_declared
|
|
_c_DEFTYPES(declare_arc2, Self, i_key);
|
|
#endif
|
|
struct _c_MEMB(_ctrl) {
|
|
catomic_long counter; // nb! counter <-> value order is swapped.
|
|
_m_value value;
|
|
};
|
|
#define ctrl ctrl2
|
|
|
|
// c++: std::make_shared<_m_value>(val)
|
|
STC_INLINE Self _c_MEMB(_make)(_m_value val) {
|
|
Self out = {.ctrl2=_i_new_n(_c_MEMB(_ctrl), 1)};
|
|
out.ctrl2->counter = 1;
|
|
out.get = &out.ctrl2->value;
|
|
*out.get = val;
|
|
return out;
|
|
}
|
|
|
|
STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* ptr) {
|
|
Self out = {.get=ptr};
|
|
if (ptr) {
|
|
enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)};
|
|
// Adds 2 dummy bytes to ensure that the second if-test in _drop() is safe.
|
|
catomic_long* _rc = (catomic_long*)i_malloc(OFFSET + 2);
|
|
out.ctrl2 = (_c_MEMB(_ctrl)*) _rc;
|
|
out.ctrl2->counter = 1;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// destructor
|
|
STC_INLINE void _c_MEMB(_drop)(const Self* self) {
|
|
if (self->ctrl2 && _i_atomic_dec_and_test(&self->ctrl2->counter)) {
|
|
enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)};
|
|
i_keydrop(self->get);
|
|
|
|
if ((char*)self->ctrl2 + OFFSET == (char*)self->get) {
|
|
i_free((void*)self->ctrl2, c_sizeof *self->ctrl2); // _make()
|
|
} else {
|
|
i_free((void*)self->ctrl2, OFFSET + 2); // _from_ptr()
|
|
i_free(self->get, c_sizeof *self->get);
|
|
}
|
|
}
|
|
}
|
|
|
|
// take ownership of pointer p
|
|
STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* ptr) {
|
|
_c_MEMB(_drop)(self);
|
|
*self = _c_MEMB(_from_ptr)(ptr);
|
|
}
|
|
|
|
#endif // ---------- end Arc2 with two pointers ------------
|
|
|
|
STC_INLINE long _c_MEMB(_use_count)(Self arc)
|
|
{ return arc.ctrl ? arc.ctrl->counter : 0; }
|
|
|
|
STC_INLINE Self _c_MEMB(_init)(void)
|
|
{ return c_literal(Self){0}; }
|
|
|
|
STC_INLINE Self _c_MEMB(_from)(_m_raw raw)
|
|
{ return _c_MEMB(_make)(i_keyfrom(raw)); }
|
|
|
|
STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self)
|
|
{ return i_keytoraw(self->get); }
|
|
|
|
// move ownership to receiving arc
|
|
STC_INLINE Self _c_MEMB(_move)(Self* self) {
|
|
Self arc = *self;
|
|
*self = (Self){0};
|
|
return arc; // now unowned
|
|
}
|
|
|
|
// take ownership of unowned arc
|
|
STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) {
|
|
_c_MEMB(_drop)(self);
|
|
*self = unowned; // now owned
|
|
}
|
|
|
|
// make shared ownership with owned arc
|
|
STC_INLINE void _c_MEMB(_assign)(Self* self, const Self* owned) {
|
|
if (owned->ctrl)
|
|
_i_atomic_inc(&owned->ctrl->counter);
|
|
_c_MEMB(_drop)(self);
|
|
*self = *owned;
|
|
}
|
|
|
|
// clone by sharing. Does not use i_keyclone, so OK to always define.
|
|
STC_INLINE Self _c_MEMB(_clone)(Self owned) {
|
|
if (owned.ctrl)
|
|
_i_atomic_inc(&owned.ctrl->counter);
|
|
return owned;
|
|
}
|
|
|
|
#if defined _i_has_cmp
|
|
STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry)
|
|
{ return i_cmp(rx, ry); }
|
|
#endif
|
|
|
|
#if defined _i_has_eq
|
|
STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry)
|
|
{ return i_eq(rx, ry); }
|
|
#endif
|
|
|
|
#if !defined i_no_hash && defined _i_has_eq
|
|
STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx)
|
|
{ return i_hash(rx); }
|
|
#endif // i_no_hash
|
|
|
|
#undef ctrl
|
|
#undef i_no_atomic
|
|
#undef i_arc2
|
|
#undef _i_atomic_inc
|
|
#undef _i_atomic_dec_and_test
|
|
#undef _i_is_arc
|
|
#include "sys/finalize.h"
|