|
|
|
/*
|
|
|
|
* 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.int.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
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
namespace upb { class RefCounted; }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted);
|
|
|
|
|
|
|
|
struct upb_refcounted_vtbl;
|
|
|
|
|
|
|
|
UPB_DEFINE_CLASS0(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, upb::RefCounted);
|
|
|
|
,
|
|
|
|
UPB_DEFINE_STRUCT0(upb_refcounted,
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
#ifdef UPB_DEBUG_REFS
|
|
|
|
upb_inttable *refs; // Maps owner -> trackedref for incoming refs.
|
|
|
|
upb_inttable *ref2s; // Set of targets for outgoing ref2s.
|
|
|
|
#endif
|
|
|
|
));
|
|
|
|
|
|
|
|
UPB_BEGIN_EXTERN_C // {
|
|
|
|
|
|
|
|
// It is better to use tracked refs when possible, for the extra debugging
|
|
|
|
// capability. But if this is not possible (because you don't have easy access
|
|
|
|
// to a stable pointer value that is associated with the ref), you can pass
|
|
|
|
// UPB_UNTRACKED_REF instead.
|
|
|
|
extern const void *UPB_UNTRACKED_REF;
|
|
|
|
|
|
|
|
// 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", or
|
|
|
|
// if the maximum depth of the graph exceeds "maxdepth", 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,
|
|
|
|
int maxdepth);
|
|
|
|
|
|
|
|
// Shared by all compiled-in refcounted objects.
|
|
|
|
extern uint32_t static_refcount;
|
|
|
|
|
|
|
|
UPB_END_EXTERN_C // }
|
|
|
|
|
|
|
|
#ifdef UPB_DEBUG_REFS
|
|
|
|
#define UPB_REFCOUNT_INIT(refs, ref2s) \
|
|
|
|
{&static_refcount, NULL, NULL, 0, true, refs, ref2s}
|
|
|
|
#else
|
|
|
|
#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
// 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_
|