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

Reply via email to