summaryrefslogtreecommitdiff
path: root/src/ref.h
blob: ea36ec7f9e883578e23aa3a945f89fe0adf9e8cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
 * \file ref.h
 * Thread-safe reference counter.
 */

#ifndef REF_H
#define REF_H

#if __GNUC__ >= 4
# define REF_DECLARE() volatile int _ref
# define REF_INIT(ptr)                          \
    ((ptr)->_ref = 1)
# define REF_FREE(ptr)
# define REF_INC(ptr)                           \
    __sync_add_and_fetch(&((ptr)->_ref), 1)
# define REF_DEC(ptr)                               \
    (__sync_sub_and_fetch(&((ptr)->_ref), 1) <= 0)

#else
# warning Unsupported compiler, horrible fallback threadsafe reference counters

# include "thread.h"

/**
 * Declare reference counter. Use in struct declaration.
 */
# define REF_DECLARE() int _ref; thread_lock_t* _reflock
/**
 * Initialize reference counter. Must be first MACRO used.
 * @param ptr pointer to the struct, may not be NULL
 * @return false in case of allocation errors
 */
# define REF_INIT(ptr)                              \
    _ref_init(&((ptr)->_ref), &((ptr)->_reflock))
/**
 * Free reference counter.
 * @param ptr pointer to the struct, may not be NULL
 */
# define REF_FREE(ptr) thread_lock_free((ptr)->_reflock)
/**
 * Increase reference counter.
 * @param ptr pointer to the struct, may not be NULL
 */
# define REF_INC(ptr) _ref_inc(&((ptr)->_ref), (ptr)->_reflock)
/**
 * Decrease reference counter.
 * @param ptr pointer to the struct, may not be NULL
 * @return true if reference counter now is zero
 */
# define REF_DEC(ptr) _ref_dec(&((ptr)->_ref), (ptr)->_reflock)

/**
 * Fallback implementation of REF_INIT.
 * @param value pointer to reference counter, may not be NULL
 * @param lock pointer to reference counter lock, may not be NULL
 * @return true if both pointers was initialized correctly
 */
static inline NONULL bool _ref_init(int* value, thread_lock_t** lock)
{
    assert(value);
    assert(lock);
    *value = 1;
    *lock = thread_lock_new();
    return *lock;
}

/**
 * Fallback implementation of REF_INC.
 * @param value pointer to reference counter, may not be NULL
 * @param lock reference counter lock, may not be NULL
 */
static inline NONULL void _ref_inc(int* value, thread_lock_t* lock)
{
    thread_lock_lock(lock);
    (*value)++;
    thread_lock_unlock(lock);
}

/**
 * Fallback implementation of REF_DEC.
 * @param value pointer to reference counter, may not be NULL
 * @param lock reference counter lock, may not be NULL
 * @return true if reference counter was decreased to 0
 */
static inline NONULL bool _ref_dec(int* value, thread_lock_t* lock)
{
    bool ret;
    thread_lock_lock(lock);
    ret = --(*value) == 0;
    thread_lock_unlock(lock);
    return ret;
}
#endif

#endif /* REF_H */