Repository: lucy-clownfish Updated Branches: refs/heads/master ab9d38830 -> d601b8b8b
Implement CFCWeakPtr We mostly avoided circular references in CFC so far, although it's often useful to have pointers to a parent or ancestor in the tree structures CFC operates on. Weak pointers allow to break circular references when destroying objects. Even after the upcoming changes, CFC won't reference objects through weak pointers after the strong refcount dropped to zero. So we could just use normal pointers and simply don't incref/decref them. The WeakPtr mechanism serves mainly as annotation and safety net, making sure that weak pointers are used as intended. Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/a86fab47 Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/a86fab47 Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/a86fab47 Branch: refs/heads/master Commit: a86fab47c43ca08883e55e55ff9d10bfc5a5017f Parents: 0e8aa92 Author: Nick Wellnhofer <wellnho...@aevum.de> Authored: Mon Feb 27 13:01:16 2017 +0100 Committer: Nick Wellnhofer <wellnho...@aevum.de> Committed: Thu Mar 2 20:06:38 2017 +0100 ---------------------------------------------------------------------- compiler/src/CFCBase.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- compiler/src/CFCBase.h | 17 +++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a86fab47/compiler/src/CFCBase.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCBase.c b/compiler/src/CFCBase.c index bce7e70..f2a8858 100644 --- a/compiler/src/CFCBase.c +++ b/compiler/src/CFCBase.c @@ -22,13 +22,20 @@ CFCBase* CFCBase_allocate(const CFCMeta *meta) { CFCBase *self = (CFCBase*)CALLOCATE(meta->obj_alloc_size, 1); self->refcount = 1; + self->weak_refcount = 0; self->meta = meta; return self; } void CFCBase_destroy(CFCBase *self) { - FREEMEM(self); + if (self->weak_refcount == 0) { + FREEMEM(self); + } + else { + // Let WeakPtr free the memory. + self->refcount = 0; + } } CFCBase* @@ -42,11 +49,15 @@ CFCBase_incref(CFCBase *self) { unsigned CFCBase_decref(CFCBase *self) { if (!self) { return 0; } - unsigned modified_refcount = --self->refcount; - if (modified_refcount == 0) { + if (self->refcount > 1) { + return --self->refcount; + } + else { + // Don't decrease refcount to 0 before calling `destroy`. This could + // make WeakPtrs free the object. self->meta->destroy(self); + return 0; } - return modified_refcount; } unsigned @@ -59,4 +70,46 @@ CFCBase_get_cfc_class(CFCBase *self) { return self->meta->cfc_class; } +CFCWeakPtr +CFCWeakPtr_new(CFCBase *base) { + CFCWeakPtr self = { base }; + if (base) { base->weak_refcount += 1; } + return self; +} + +CFCBase* +CFCWeakPtr_deref(CFCWeakPtr self) { + if (self.ptr_ != NULL && self.ptr_->refcount == 0) { + // CFC doesn't access WeakPtrs after the strong refcount went to + // zero, so throw an exception. + CFCUtil_die("Invalid WeakPtr deref"); + } + return self.ptr_; +} + +void +CFCWeakPtr_set(CFCWeakPtr *self, CFCBase *base) { + CFCBase *old_base = self->ptr_; + if (old_base == base) { return; } + if (old_base != NULL) { + old_base->weak_refcount -= 1; + if (old_base->refcount == 0 && old_base->weak_refcount == 0) { + FREEMEM(old_base); + } + } + self->ptr_ = base; + if (base) { base->weak_refcount += 1; } +} + +void +CFCWeakPtr_destroy(CFCWeakPtr *self) { + CFCBase *base = self->ptr_; + if (base == NULL) { return; } + base->weak_refcount -= 1; + if (base->refcount == 0 && base->weak_refcount == 0) { + FREEMEM(base); + } + self->ptr_ = NULL; +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/a86fab47/compiler/src/CFCBase.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCBase.h b/compiler/src/CFCBase.h index 09ee662..43ea774 100644 --- a/compiler/src/CFCBase.h +++ b/compiler/src/CFCBase.h @@ -28,12 +28,14 @@ extern "C" { typedef struct CFCBase CFCBase; typedef struct CFCMeta CFCMeta; +typedef struct CFCWeakPtr CFCWeakPtr; typedef void (*CFCBase_destroy_t)(CFCBase *self); #ifdef CFC_NEED_BASE_STRUCT_DEF struct CFCBase { const CFCMeta *meta; unsigned refcount; + unsigned weak_refcount; }; #endif struct CFCMeta { @@ -41,6 +43,9 @@ struct CFCMeta { size_t obj_alloc_size; CFCBase_destroy_t destroy; }; +struct CFCWeakPtr { + CFCBase *ptr_; +}; /** Allocate a new CFC object. * @@ -81,6 +86,18 @@ CFCBase_get_refcount(CFCBase *self); const char* CFCBase_get_cfc_class(CFCBase *self); +CFCWeakPtr +CFCWeakPtr_new(CFCBase *base); + +CFCBase* +CFCWeakPtr_deref(CFCWeakPtr self); + +void +CFCWeakPtr_set(CFCWeakPtr *self, CFCBase *base); + +void +CFCWeakPtr_destroy(CFCWeakPtr *self); + #ifdef __cplusplus } #endif