summaryrefslogtreecommitdiff
path: root/src/upb_atomic.h
blob: 87d06febe19161d9ab68a635f773c7dfb12bf2ec (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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Copyright (c) 2009 Joshua Haberman.  See LICENSE for details.
 *
 * Only a very small part of upb is thread-safe.  Notably, individual
 * messages, arrays, and strings are *not* thread safe for mutating.
 * However, we do make message *metadata* such as upb_msgdef and
 * upb_context thread-safe, and their ownership is tracked via atomic
 * refcounting.  This header implements the small number of atomic
 * primitives required to support this.  The primitives we implement
 * are:
 *
 * - a reader/writer lock (wrappers around platform-provided mutexes).
 * - an atomic refcount.
 */

#ifndef UPB_ATOMIC_H_
#define UPB_ATOMIC_H_

#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/* inline if possible, emit standalone code if required. */
#ifndef INLINE
#define INLINE static inline
#endif

// Until this stuff is actually working, make thread-unsafe the default.
#define UPB_THREAD_UNSAFE

#ifdef UPB_THREAD_UNSAFE

/* Non-thread-safe implementations. ******************************************/

typedef struct {
  int v;
} upb_atomic_refcount_t;

INLINE void upb_atomic_refcount_init(upb_atomic_refcount_t *a, int val) {
  a->v = val;
}

INLINE bool upb_atomic_ref(upb_atomic_refcount_t *a) {
  return a->v++ == 0;
}

INLINE bool upb_atomic_unref(upb_atomic_refcount_t *a) {
  return --a->v == 0;
}

INLINE int upb_atomic_read(upb_atomic_refcount_t *a) {
  return a->v;
}

INLINE bool upb_atomic_add(upb_atomic_refcount_t *a, int val) {
  a->v += val;
  return a->v == 0;
}

INLINE int upb_atomic_fetch_and_add(upb_atomic_refcount_t *a, int val) {
  int ret = a->v;
  a->v += val;
  return ret;
}

#endif

/* Atomic refcount ************************************************************/

#ifdef UPB_THREAD_UNSAFE

/* Already defined above. */

#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4

/* GCC includes atomic primitives. */

typedef struct {
  volatile int v;
} upb_atomic_refcount_t;

INLINE void upb_atomic_refcount_init(upb_atomic_refcount_t *a, int val) {
  a->v = val;
  __sync_synchronize();   /* Ensure the initialized value is visible. */
}

INLINE bool upb_atomic_ref(upb_atomic_refcount_t *a) {
  return __sync_fetch_and_add(&a->v, 1) == 0;
}

INLINE bool upb_atomic_add(upb_atomic_refcount_t *a, int n) {
  return __sync_add_and_fetch(&a->v, n) == 0;
}

INLINE bool upb_atomic_unref(upb_atomic_refcount_t *a) {
  return __sync_sub_and_fetch(&a->v, 1) == 0;
}

INLINE bool upb_atomic_read(upb_atomic_refcount_t *a) {
  return __sync_fetch_and_add(&a->v, 0);
}

#elif defined(WIN32)

/* Windows defines atomic increment/decrement. */
#include <Windows.h>

typedef struct {
  volatile LONG val;
} upb_atomic_refcount_t;

INLINE void upb_atomic_refcount_init(upb_atomic_refcount_t *a, int val) {
  InterlockedExchange(&a->val, val);
}

INLINE bool upb_atomic_ref(upb_atomic_refcount_t *a) {
  return InterlockedIncrement(&a->val) == 1;
}

INLINE bool upb_atomic_unref(upb_atomic_refcount_t *a) {
  return InterlockedDecrement(&a->val) == 0;
}

#else
#error Atomic primitives not defined for your platform/CPU.  \
       Implement them or compile with UPB_THREAD_UNSAFE.
#endif

INLINE bool upb_atomic_only(upb_atomic_refcount_t *a) {
  return upb_atomic_read(a) == 1;
}

/* Reader/Writer lock. ********************************************************/

#ifdef UPB_THREAD_UNSAFE

typedef struct {
} upb_rwlock_t;

INLINE void upb_rwlock_init(upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_destroy(upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_rdlock(upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_wrlock(upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_unlock(upb_rwlock_t *l) { (void)l; }

#elif defined(UPB_USE_PTHREADS)

#include <pthread.h>

typedef struct {
  pthread_rwlock_t lock;
} upb_rwlock_t;

INLINE void upb_rwlock_init(upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_init(&l->lock, NULL);
}

INLINE void upb_rwlock_destroy(upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_destroy(&l->lock);
}

INLINE void upb_rwlock_rdlock(upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_rdlock(&l->lock);
}

INLINE void upb_rwlock_wrlock(upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_wrlock(&l->lock);
}

INLINE void upb_rwlock_unlock(upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_unlock(&l->lock);
}

#else
#error Reader/writer lock is not defined for your platform/CPU.  \
       Implement it or compile with UPB_THREAD_UNSAFE.
#endif

#ifdef __cplusplus
}  /* extern "C" */
#endif

#endif  /* UPB_ATOMIC_H_ */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback