summaryrefslogtreecommitdiff
path: root/upb/atomic.h
blob: 2478fe493898146fdf5d2c4968c5efa9e2699124 (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
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Copyright (c) 2009 Google Inc.  See LICENSE for details.
 * Author: Josh Haberman <jhaberman@gmail.com>
 *
 * 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_symtab 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.
 *
 * TODO: This needs some revisiting/refinement, see:
 *       http://code.google.com/p/upb/issues/detail?id=8
 */

#ifndef UPB_ATOMIC_H_
#define UPB_ATOMIC_H_

#include <stdbool.h>
#include <assert.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_t;

#define UPB_ATOMIC_INIT(x) {x}

INLINE void upb_atomic_init(upb_atomic_t *a, int val) { a->v = val; }
INLINE bool upb_atomic_ref(upb_atomic_t *a) { return a->v++ == 0; }
INLINE bool upb_atomic_unref(upb_atomic_t *a) { assert(a->v > 0); return --a->v == 0; }
INLINE int upb_atomic_read(upb_atomic_t *a) { return a->v; }
INLINE bool upb_atomic_add(upb_atomic_t *a, int val) {
  a->v += val;
  return a->v == 0;
}

#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_t;

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

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

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

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

INLINE bool upb_atomic_read(upb_atomic_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_t;

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

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

INLINE bool upb_atomic_unref(upb_atomic_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_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(const upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_destroy(const upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_rdlock(const upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_wrlock(const upb_rwlock_t *l) { (void)l; }
INLINE void upb_rwlock_unlock(const 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(const upb_rwlock_t *l) {
  /* TODO: check return value. */
  pthread_rwlock_init(&l->lock, NULL);
}

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

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

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

INLINE void upb_rwlock_unlock(const 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