summaryrefslogtreecommitdiff
path: root/upb/refcounted.h
blob: 19993ca1e6fe529846584720a2183b077c79295a (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
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Copyright (c) 2009-2012 Google Inc.  See LICENSE for details.
 * Author: Josh Haberman <jhaberman@gmail.com>
 *
 * A refcounting scheme that supports circular refs.  It accomplishes this by
 * partitioning the set of objects into groups such that no cycle spans groups;
 * we can then reference-count the group as a whole and ignore refs within the
 * group.  When objects are mutable, these groups are computed very
 * conservatively; we group any objects that have ever had a link between them.
 * When objects are frozen, we compute strongly-connected components which
 * allows us to be precise and only group objects that are actually cyclic.
 *
 * This is a mixed C/C++ interface that offers a full API to both languages.
 * See the top-level README for more information.
 */

#ifndef UPB_REFCOUNTED_H_
#define UPB_REFCOUNTED_H_

#include "upb/table.h"

// Reference tracking is designed to be used with a tool like Valgrind; when
// enabled, it will cause reference leaks to show up as actual memory leaks
// that are attributed to the code that leaked the ref, *not* the code that
// originally created the object.
#ifndef NDEBUG
#define UPB_DEBUG_REFS
#endif

struct upb_refcounted_vtbl;

#ifdef __cplusplus
namespace upb { class RefCounted; }
typedef upb::RefCounted upb_refcounted;
extern "C" {
#else
struct upb_refcounted;
typedef struct upb_refcounted upb_refcounted;
#endif

#ifdef __cplusplus

class upb::RefCounted {
 public:
  // Returns true if the given object is frozen.
  bool IsFrozen() const;

  // Increases the ref count, the new ref is owned by "owner" which must not
  // already own a ref (and should not itself be a refcounted object if the ref
  // could possibly be circular; see below).
  // Thread-safe iff "this" is frozen.
  void Ref(const void *owner) const;

  // Release a ref that was acquired from upb_refcounted_ref() and collects any
  // objects it can.
  void Unref(const void *owner) const;

  // Moves an existing ref from "from" to "to", without changing the overall
  // ref count.  DonateRef(foo, NULL, owner) is the same as Ref(foo, owner),
  // but "to" may not be NULL.
  void DonateRef(const void *from, const void *to) const;

  // Verifies that a ref to the given object is currently held by the given
  // owner.  Only effective in UPB_DEBUG_REFS builds.
  void CheckRef(const void *owner) const;

 private:
  UPB_DISALLOW_POD_OPS(RefCounted);
#else
struct upb_refcounted {
#endif
  // A single reference count shared by all objects in the group.
  uint32_t *group;

  // A singly-linked list of all objects in the group.
  upb_refcounted *next;

  // Table of function pointers for this type.
  const struct upb_refcounted_vtbl *vtbl;

  // Maintained only when mutable, this tracks the number of refs (but not
  // ref2's) to this object.  *group should be the sum of all individual_count
  // in the group.
  uint32_t individual_count;

  bool is_frozen;
};

// Native C API.
bool upb_refcounted_isfrozen(const upb_refcounted *r);
void upb_refcounted_ref(const upb_refcounted *r, const void *owner);
void upb_refcounted_unref(const upb_refcounted *r, const void *owner);
void upb_refcounted_donateref(
    const upb_refcounted *r, const void *from, const void *to);
void upb_refcounted_checkref(const upb_refcounted *r, const void *owner);


// Internal-to-upb Interface ///////////////////////////////////////////////////

typedef void upb_refcounted_visit(const upb_refcounted *r,
                                  const upb_refcounted *subobj,
                                  void *closure);

struct upb_refcounted_vtbl {
  // Must visit all subobjects that are currently ref'd via upb_refcounted_ref2.
  // Must be longjmp()-safe.
  void (*visit)(const upb_refcounted *r, upb_refcounted_visit *visit, void *c);

  // Must free the object and release all references to other objects.
  void (*free)(upb_refcounted *r);
};

// Initializes the refcounted with a single ref for the given owner.  Returns
// false if memory could not be allocated.
bool upb_refcounted_init(upb_refcounted *r,
                         const struct upb_refcounted_vtbl *vtbl,
                         const void *owner);

// Adds a ref from one refcounted object to another ("from" must not already
// own a ref).  These refs may be circular; cycles will be collected correctly
// (if conservatively).  These refs do not need to be freed in from's free()
// function.
void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from);

// Removes a ref that was acquired from upb_refcounted_ref2(), and collects any
// object it can.  This is only necessary when "from" no longer points to "r",
// and not from from's "free" function.
void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from);

#define upb_ref2(r, from) \
    upb_refcounted_ref2((const upb_refcounted*)r, (upb_refcounted*)from)
#define upb_unref2(r, from) \
    upb_refcounted_unref2((const upb_refcounted*)r, (upb_refcounted*)from)

// Freezes all mutable object reachable by ref2() refs from the given roots.
// This will split refcounting groups into precise SCC groups, so that
// refcounting of frozen objects can be more aggressive.  If memory allocation
// fails or if more than 2**31 mutable objects are reachable from "roots",
// false is returned and the objects are unchanged.
//
// After this operation succeeds, the objects are frozen/const, and may not be
// used through non-const pointers.  In particular, they may not be passed as
// the second parameter of upb_refcounted_{ref,unref}2().  On the upside, all
// operations on frozen refcounteds are threadsafe, and objects will be freed
// at the precise moment that they become unreachable.
//
// Caller must own refs on each object in the "roots" list.
bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s);

// Shared by all compiled-in refcounted objects.
extern uint32_t static_refcount;

#define UPB_REFCOUNT_INIT {&static_refcount, NULL, NULL, 0, true}

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

// C++ Wrappers.
namespace upb {
inline bool RefCounted::IsFrozen() const {
  return upb_refcounted_isfrozen(this);
}
inline void RefCounted::Ref(const void *owner) const {
  upb_refcounted_ref(this, owner);
}
inline void RefCounted::Unref(const void *owner) const {
  upb_refcounted_unref(this, owner);
}
inline void RefCounted::DonateRef(const void *from, const void *to) const {
  upb_refcounted_donateref(this, from, to);
}
inline void RefCounted::CheckRef(const void *owner) const {
  upb_refcounted_checkref(this, owner);
}
}  // namespace upb
#endif

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