Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hide refcount implementation into .c file #552

Merged
merged 14 commits into from
Jul 22, 2024
30 changes: 14 additions & 16 deletions include/zenoh-pico/api/olv_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
#ifndef INCLUDE_ZENOH_PICO_API_OLV_MACROS_H
#define INCLUDE_ZENOH_PICO_API_OLV_MACROS_H

// Gets internal value from refcounted type (e.g. z_loaned_session_t, z_query_t)
#define _Z_RC_IN_VAL(arg) ((arg)->_val)

// Checks if refcounted type is initialized
#define _Z_RC_IS_NULL(arg) ((arg)->_cnt == NULL)

// Gets internal value from refcounted owned type (e.g. z_owned_session_t, z_owned_query_t)
#define _Z_OWNED_RC_IN_VAL(arg) ((arg)->_rc._val)

// Owned/Loaned/View type macros
//
// !!! FOR INTERNAL USAGE ONLY !!!
Expand Down Expand Up @@ -132,24 +141,22 @@
_Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_INNER(type, name, f_check, f_null, f_drop, static inline)

#define _Z_OWNED_FUNCTIONS_RC_IMPL(name) \
_Bool z_##name##_check(const z_owned_##name##_t *val) { return val->_rc.in != NULL; } \
_Bool z_##name##_check(const z_owned_##name##_t *val) { return !_Z_RC_IS_NULL(&val->_rc); } \
const z_loaned_##name##_t *z_##name##_loan(const z_owned_##name##_t *val) { return &val->_rc; } \
z_loaned_##name##_t *z_##name##_loan_mut(z_owned_##name##_t *val) { return &val->_rc; } \
void z_##name##_null(z_owned_##name##_t *val) { val->_rc.in = NULL; } \
void z_##name##_null(z_owned_##name##_t *val) { val->_rc = _z_##name##_rc_null(); } \
z_owned_##name##_t *z_##name##_move(z_owned_##name##_t *val) { return val; } \
int8_t z_##name##_clone(z_owned_##name##_t *obj, const z_loaned_##name##_t *src) { \
int8_t ret = _Z_RES_OK; \
obj->_rc = _z_##name##_rc_clone((z_loaned_##name##_t *)src); \
if (obj->_rc.in == NULL) { \
if (_Z_RC_IS_NULL(&obj->_rc)) { \
ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; \
} \
return ret; \
} \
void z_##name##_drop(z_owned_##name##_t *val) { \
if (val->_rc.in != NULL) { \
if (_z_##name##_rc_drop(&val->_rc)) { \
val->_rc.in = NULL; \
} \
if (!_Z_RC_IS_NULL(&val->_rc)) { \
_z_##name##_rc_drop(&val->_rc); \
} \
}

Expand Down Expand Up @@ -197,13 +204,4 @@
return _Z_RES_OK; \
}

// Gets internal value from refcounted type (e.g. z_loaned_session_t, z_query_t)
#define _Z_RC_IN_VAL(arg) ((arg)->in->val)

// Checks if refcounted type is initialized
#define _Z_RC_IS_NULL(arg) ((arg)->in == NULL)

// Gets internal value from refcounted owned type (e.g. z_owned_session_t, z_owned_query_t)
#define _Z_OWNED_RC_IN_VAL(arg) ((arg)->_rc.in->val)

#endif /* INCLUDE_ZENOH_PICO_API_OLV_MACROS_H */
457 changes: 141 additions & 316 deletions include/zenoh-pico/collections/refcount.h

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions include/zenoh-pico/utils/result.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ typedef enum {
_Z_ERR_DID_NOT_READ = -76,
_Z_ERR_INVALID = -75,
Z_EINVAL = -75,
_Z_ERR_OVERFLOW = -74,

_Z_ERR_GENERIC = -128
} _z_res_t;
Expand Down
120 changes: 64 additions & 56 deletions src/api/api.c

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions src/collections/arc_slice.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,42 @@ _z_arc_slice_t _z_arc_slice_empty(void) {
_z_arc_slice_t s;
s.len = 0;
s.start = 0;
s.slice.in = NULL;
s.slice = _z_slice_rc_null();
return s;
}

_z_arc_slice_t _z_arc_slice_wrap(_z_slice_t s, size_t offset, size_t len) {
assert(offset + len <= s.len);

_z_arc_slice_t arc_s;
arc_s.slice = _z_slice_rc_new_from_val(s);

arc_s.slice = _z_slice_rc_new_from_val(&s);
if (_Z_RC_IS_NULL(&arc_s.slice)) {
return _z_arc_slice_empty();
}
arc_s.len = len;
arc_s.start = offset;
return arc_s;
}

_z_arc_slice_t _z_arc_slice_get_subslice(const _z_arc_slice_t* s, size_t offset, size_t len) {
assert(offset + len <= s->len);
assert(s->slice.in != NULL || (len == 0 && offset == 0));
assert(!_Z_RC_IS_NULL(&s->slice) || (len == 0 && offset == 0));

_z_arc_slice_t out;
if (s->slice.in == NULL) {
out.slice.in = NULL;
out.start = 0;
out.len = 0;
} else {
out.slice = _z_slice_rc_clone(&s->slice);
out.len = len;
out.start = s->start + offset;
if (_Z_RC_IS_NULL(&s->slice)) {
return _z_arc_slice_empty();
}
_z_arc_slice_t out;
out.slice = _z_slice_rc_clone(&s->slice);
out.len = len;
out.start = s->start + offset;
return out;
}

size_t _z_arc_slice_len(const _z_arc_slice_t* s) { return s->len; }

_Bool _z_arc_slice_is_empty(const _z_arc_slice_t* s) { return _z_arc_slice_len(s) == 0; }

const uint8_t* _z_arc_slice_data(const _z_arc_slice_t* s) { return s->slice.in->val.start + s->start; }
const uint8_t* _z_arc_slice_data(const _z_arc_slice_t* s) { return _Z_RC_IN_VAL(&s->slice)->start + s->start; }

int8_t _z_arc_slice_copy(_z_arc_slice_t* dst, const _z_arc_slice_t* src) {
_z_slice_rc_copy(&dst->slice, &src->slice);
Expand All @@ -73,7 +73,7 @@ int8_t _z_arc_slice_move(_z_arc_slice_t* dst, _z_arc_slice_t* src) {
dst->start = src->start;
src->len = 0;
src->start = 0;
src->slice.in = NULL;
src->slice = _z_slice_rc_null();
return _Z_RES_OK;
}

Expand Down
10 changes: 5 additions & 5 deletions src/collections/bytes.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ int8_t _z_bytes_reader_read_slices(_z_bytes_reader_t *reader, size_t len, _z_byt
reader->in_slice_idx = 0;
}

if (ss.slice.in == NULL) {
if (_Z_RC_IS_NULL(&ss.slice)) {
res = _Z_ERR_SYSTEM_OUT_OF_MEMORY;
break;
}
Expand Down Expand Up @@ -432,8 +432,8 @@ int8_t _z_bytes_writer_ensure_cache(_z_bytes_writer_t *writer) {
// first we check if cache stayed untouched since previous write operation
if (writer->cache != NULL) {
_z_arc_slice_t *arc_s = _z_bytes_get_slice(writer->bytes, _z_bytes_num_slices(writer->bytes) - 1);
if (_Z_RC_IN_VAL(&arc_s->slice).start + arc_s->len == writer->cache) {
size_t remaining_in_cache = _Z_RC_IN_VAL(&arc_s->slice).len - arc_s->len;
if (_Z_RC_IN_VAL(&arc_s->slice)->start + arc_s->len == writer->cache) {
size_t remaining_in_cache = _Z_RC_IN_VAL(&arc_s->slice)->len - arc_s->len;
if (remaining_in_cache > 0) return _Z_RES_OK;
}
}
Expand All @@ -448,7 +448,7 @@ int8_t _z_bytes_writer_ensure_cache(_z_bytes_writer_t *writer) {
}

_Z_CLEAN_RETURN_IF_ERR(_z_bytes_append_slice(writer->bytes, &cache), _z_arc_slice_drop(&cache));
writer->cache = (uint8_t *)_Z_RC_IN_VAL(&cache.slice).start;
writer->cache = (uint8_t *)_Z_RC_IN_VAL(&cache.slice)->start;
return _Z_RES_OK;
}

Expand All @@ -467,7 +467,7 @@ int8_t _z_bytes_writer_write(_z_bytes_writer_t *writer, const uint8_t *src, size
while (len > 0) {
_Z_RETURN_IF_ERR(_z_bytes_writer_ensure_cache(writer));
_z_arc_slice_t *arc_s = _z_bytes_get_slice(writer->bytes, _z_bytes_num_slices(writer->bytes) - 1);
size_t remaining_in_cache = _Z_RC_IN_VAL(&arc_s->slice).len - arc_s->len;
size_t remaining_in_cache = _Z_RC_IN_VAL(&arc_s->slice)->len - arc_s->len;
size_t to_copy = remaining_in_cache < len ? remaining_in_cache : len;
memcpy(writer->cache, src, to_copy);
len -= to_copy;
Expand Down
221 changes: 221 additions & 0 deletions src/collections/refcount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
//
// Copyright (c) 2024 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
//

#include "zenoh-pico/collections/refcount.h"

#include "zenoh-pico/utils/logging.h"

#define _Z_RC_MAX_COUNT INT32_MAX // Based on Rust lazy overflow check

#if Z_FEATURE_MULTI_THREAD == 1
#if ZENOH_C_STANDARD != 99

#ifndef __cplusplus
#include <stdatomic.h>
#define _z_atomic(X) _Atomic X
#define _z_atomic_store_explicit atomic_store_explicit
#define _z_atomic_fetch_add_explicit atomic_fetch_add_explicit
#define _z_atomic_fetch_sub_explicit atomic_fetch_sub_explicit
#define _z_atomic_load_explicit atomic_load_explicit
#define _z_atomic_compare_exchange_strong atomic_compare_exchange_strong
#define _z_atomic_compare_exchange_weak_explicit atomic_compare_exchange_weak_explicit
#define _z_memory_order_acquire memory_order_acquire
#define _z_memory_order_release memory_order_release
#define _z_memory_order_relaxed memory_order_relaxed
#else
#include <atomic>
#define _z_atomic(X) std::atomic<X>
#define _z_atomic_store_explicit std::atomic_store_explicit
#define _z_atomic_fetch_add_explicit std::atomic_fetch_add_explicit
#define _z_atomic_fetch_sub_explicit std::atomic_fetch_sub_explicit
#define _z_atomic_load_explicit std::atomic_load_explicit
#define _z_atomic_compare_exchange_strong std::atomic_compare_exchange_strong
#define _z_atomic_compare_exchange_weak_explicit std::atomic_compare_exchange_weak_explicit
#define _z_memory_order_acquire std::memory_order_acquire
#define _z_memory_order_release std::memory_order_release
#define _z_memory_order_relaxed std::memory_order_relaxed
#endif // __cplusplus

// c11 atomic variant
#define _ZP_RC_CNT_TYPE _z_atomic(unsigned int)
#define _ZP_RC_OP_INIT_CNT(p) \
_z_atomic_store_explicit(&(p)->_strong_cnt, (unsigned int)1, _z_memory_order_relaxed); \
_z_atomic_store_explicit(&(p)->_weak_cnt, (unsigned int)1, _z_memory_order_relaxed);
#define _ZP_RC_OP_INCR_STRONG_CNT(p) \
_z_atomic_fetch_add_explicit(&(p)->_strong_cnt, (unsigned int)1, _z_memory_order_relaxed);
#define _ZP_RC_OP_INCR_AND_CMP_WEAK(p, x) \
_z_atomic_fetch_add_explicit(&(p)->_weak_cnt, (unsigned int)1, _z_memory_order_relaxed) >= x
#define _ZP_RC_OP_DECR_AND_CMP_STRONG(p, x) \
_z_atomic_fetch_sub_explicit(&(p)->_strong_cnt, (unsigned int)1, _z_memory_order_release) > (unsigned int)x
#define _ZP_RC_OP_DECR_AND_CMP_WEAK(p, x) \
_z_atomic_fetch_sub_explicit(&(p)->_weak_cnt, (unsigned int)1, _z_memory_order_release) > (unsigned int)x
#define _ZP_RC_OP_CHECK_STRONG_CNT(p, x) _z_atomic_compare_exchange_strong(&(p)->_strong_cnt, &x, x)
#define _ZP_RC_OP_SYNC atomic_thread_fence(_z_memory_order_acquire);
#define _ZP_RC_OP_UPGRADE_CAS_LOOP \
int8_t _upgrade(_z_inner_rc_t* cnt) { \
unsigned int prev = _z_atomic_load_explicit(&cnt->_strong_cnt, _z_memory_order_relaxed); \
while ((prev != 0) && (prev < _Z_RC_MAX_COUNT)) { \
if (_z_atomic_compare_exchange_weak_explicit(&cnt->_strong_cnt, &prev, prev + 1, _z_memory_order_acquire, \
_z_memory_order_relaxed)) { \
if (_ZP_RC_OP_INCR_AND_CMP_WEAK(cnt, _Z_RC_MAX_COUNT)) { \
_Z_ERROR("Rc weak count overflow"); \
return _Z_ERR_OVERFLOW; \
} \
return _Z_RES_OK; \
} \
} \
return _Z_ERR_INVALID; \
}

#else // ZENOH_C_STANDARD == 99
#ifdef ZENOH_COMPILER_GCC

// c99 gcc sync builtin variant
#define _ZP_RC_CNT_TYPE unsigned int
#define _ZP_RC_OP_INIT_CNT(p) \
__sync_fetch_and_and(&(p)->_strong_cnt, (unsigned int)0); \
__sync_fetch_and_add(&(p)->_strong_cnt, (unsigned int)1); \
__sync_fetch_and_and(&(p)->_weak_cnt, (unsigned int)0); \
__sync_fetch_and_add(&(p)->_weak_cnt, (unsigned int)1);
#define _ZP_RC_OP_INCR_STRONG_CNT(p) __sync_fetch_and_add(&(p)->_strong_cnt, (unsigned int)1);
#define _ZP_RC_OP_INCR_AND_CMP_WEAK(p, x) __sync_fetch_and_add(&(p)->_weak_cnt, (unsigned int)1) >= x
#define _ZP_RC_OP_DECR_AND_CMP_STRONG(p, x) __sync_fetch_and_sub(&(p)->_strong_cnt, (unsigned int)1) > (unsigned int)x
#define _ZP_RC_OP_DECR_AND_CMP_WEAK(p, x) __sync_fetch_and_sub(&(p)->_weak_cnt, (unsigned int)1) > (unsigned int)x
#define _ZP_RC_OP_CHECK_STRONG_CNT(p, x) __sync_bool_compare_and_swap(&(p)->_strong_cnt, x, x)
#define _ZP_RC_OP_SYNC __sync_synchronize();
#define _ZP_RC_OP_UPGRADE_CAS_LOOP \
int8_t _upgrade(_z_inner_rc_t* cnt) { \
unsigned int prev = __sync_fetch_and_add(&cnt->_strong_cnt, (unsigned int)0); \
while ((prev != 0) && (prev < _Z_RC_MAX_COUNT)) { \
if (__sync_bool_compare_and_swap(&cnt->_strong_cnt, prev, prev + 1)) { \
if (_ZP_RC_OP_INCR_AND_CMP_WEAK(cnt, _Z_RC_MAX_COUNT)) { \
_Z_ERROR("Rc weak count overflow"); \
return _Z_ERR_OVERFLOW; \
} \
\
return _Z_RES_OK; \
} else { \
prev = __sync_fetch_and_add(&cnt->_strong_cnt, (unsigned int)0); \
} \
} \
return _Z_ERR_INVALID; \
}

#else // !ZENOH_COMPILER_GCC

// None variant
#error "Multi-thread refcount in C99 only exists for GCC, use GCC or C11 or deactivate multi-thread"
#define _ZP_RC_CNT_TYPE unsigned int
#define _ZP_RC_OP_INIT_CNT(p)
#define _ZP_RC_OP_INCR_STRONG_CNT(p)
#define _ZP_RC_OP_INCR_AND_CMP_WEAK(p, x) (x == 0)
#define _ZP_RC_OP_DECR_AND_CMP_STRONG(p, x) (x == 0)
#define _ZP_RC_OP_DECR_AND_CMP_WEAK(p, x) (x == 0)
#define _ZP_RC_OP_CHECK_STRONG_CNT(p, x) (x == 0) && (p != NULL)
#define _ZP_RC_OP_SYNC
#define _ZP_RC_OP_UPGRADE_CAS_LOOP \
int8_t _upgrade(_z_inner_rc_t* cnt) { \
(void)cnt; \
return _Z_ERR_INVALID; \
}

#endif // ZENOH_COMPILER_GCC
#endif // ZENOH_C_STANDARD != 99
#else // Z_FEATURE_MULTI_THREAD == 0

// Single thread variant
#define _ZP_RC_CNT_TYPE unsigned int
#define _ZP_RC_OP_INIT_CNT(p) \
(p)->_strong_cnt = (unsigned int)1; \
(p)->_weak_cnt = (unsigned int)1;
#define _ZP_RC_OP_INCR_STRONG_CNT(p) p->_strong_cnt++;
#define _ZP_RC_OP_INCR_AND_CMP_WEAK(p, x) p->_weak_cnt++ >= x
#define _ZP_RC_OP_DECR_AND_CMP_STRONG(p, x) p->_strong_cnt-- > (unsigned int)x
#define _ZP_RC_OP_DECR_AND_CMP_WEAK(p, x) p->_weak_cnt-- > (unsigned int)x
#define _ZP_RC_OP_CHECK_STRONG_CNT(p, x) (p->_strong_cnt == x)
#define _ZP_RC_OP_SYNC
#define _ZP_RC_OP_UPGRADE_CAS_LOOP \
int8_t _upgrade(_z_inner_rc_t* cnt) { \
if ((cnt->_strong_cnt != 0) && (cnt->_strong_cnt < _Z_RC_MAX_COUNT)) { \
if (_ZP_RC_OP_INCR_AND_CMP_WEAK(cnt, _Z_RC_MAX_COUNT)) { \
_Z_ERROR("Rc weak count overflow"); \
return _Z_ERR_OVERFLOW; \
} \
_ZP_RC_OP_INCR_STRONG_CNT(cnt) \
return _Z_RES_OK; \
} \
return _Z_ERR_OVERFLOW; \
}

#endif // Z_FEATURE_MULTI_THREAD == 1

typedef struct {
_ZP_RC_CNT_TYPE _strong_cnt;
_ZP_RC_CNT_TYPE _weak_cnt;
} _z_inner_rc_t;

int8_t _z_rc_init(void** cnt) {
*cnt = z_malloc(sizeof(_z_inner_rc_t));
if ((*cnt) == NULL) {
return _Z_ERR_SYSTEM_OUT_OF_MEMORY;
}
_ZP_RC_OP_INIT_CNT((_z_inner_rc_t*)*cnt)
return _Z_RES_OK;
}

int8_t _z_rc_increase_strong(void* cnt) {
_z_inner_rc_t* c = (_z_inner_rc_t*)cnt;
if (_ZP_RC_OP_INCR_AND_CMP_WEAK(c, _Z_RC_MAX_COUNT)) {
_Z_ERROR("Rc weak count overflow");
return _Z_ERR_OVERFLOW;
}
_ZP_RC_OP_INCR_STRONG_CNT(c);
return _Z_RES_OK;
}

int8_t _z_rc_increase_weak(void* cnt) {
_z_inner_rc_t* c = (_z_inner_rc_t*)cnt;
if (_ZP_RC_OP_INCR_AND_CMP_WEAK(c, _Z_RC_MAX_COUNT)) {
_Z_ERROR("Rc weak count overflow");
return _Z_ERR_OVERFLOW;
}
return _Z_RES_OK;
}

_Bool _z_rc_decrease_strong(void** cnt) {
_z_inner_rc_t* c = (_z_inner_rc_t*)*cnt;
if (_ZP_RC_OP_DECR_AND_CMP_STRONG(c, 1)) {
return _z_rc_decrease_weak(cnt);
}
return _z_rc_decrease_weak(cnt);
}

_Bool _z_rc_decrease_weak(void** cnt) {
_z_inner_rc_t* c = (_z_inner_rc_t*)*cnt;
if (_ZP_RC_OP_DECR_AND_CMP_WEAK(c, 1)) {
return false;
}
_ZP_RC_OP_SYNC
z_free(*cnt);
*cnt = NULL;
return true;
}

_ZP_RC_OP_UPGRADE_CAS_LOOP

int8_t _z_rc_weak_upgrade(void* cnt) { return _upgrade((_z_inner_rc_t*)cnt); }

size_t _z_rc_weak_count(void* cnt) { return ((_z_inner_rc_t*)cnt)->_weak_cnt; }

size_t _z_rc_strong_count(void* cnt) { return ((_z_inner_rc_t*)cnt)->_strong_cnt; }
Loading
Loading