This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch main
in repository ego.

View the commit online.

commit 7ff9e091ccfbaa6610b7a5e99d4c6621a40254e5
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 1 10:39:12 2026 -0600

    feat(efl): add Eina_Future to channel wrapper
    
    Add FutureToChannel wrapping Eina_Future* into <-chan *efl.Value.
    The future's then-callback sends the resolved value on the channel
    and closes it. Used by methods returning future<T>.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 efl/future.go | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 125 insertions(+)

diff --git a/efl/future.go b/efl/future.go
new file mode 100644
index 0000000..ed53df1
--- /dev/null
+++ b/efl/future.go
@@ -0,0 +1,125 @@
+package efl
+
+/*
+#cgo pkg-config: efl-ui
+
+#include <Eina.h>
+
+// Forward declaration of the Go trampoline so _ego_future_cb can call it.
+extern void egoFutureResolved(void *data, Eina_Value *value);
+
+// _ego_future_cb is the Eina_Future_Cb used by _ego_future_then. It forwards
+// the resolved value to the Go trampoline and returns an empty Eina_Value so
+// EFL knows the future chain is complete.
+static Eina_Value _ego_future_cb(void *data, const Eina_Value value,
+                                  const Eina_Future *dead_future) {
+    egoFutureResolved(data, (Eina_Value *)&value);
+    Eina_Value empty = {NULL, {{0}}};
+    return empty;
+}
+
+// _ego_future_then attaches _ego_future_cb to f, passing id (the callback map
+// key) as the user-data pointer. Uses the non-variadic
+// eina_future_then_from_desc to avoid CGO variadic limitations.
+static Eina_Future *_ego_future_then(Eina_Future *f, uintptr_t id) {
+    Eina_Future_Desc desc = {
+        .cb      = _ego_future_cb,
+        .data    = "" *)(uintptr_t)id,
+        .storage = NULL,
+    };
+    return eina_future_then_from_desc(f, desc);
+}
+
+// Thin wrappers for inline functions that cgo cannot call directly.
+
+static void _ego_future_value_free(Eina_Value *v) {
+    eina_value_free(v);
+}
+
+static Eina_Value *_ego_future_value_new(const Eina_Value_Type *type) {
+    return eina_value_new(type);
+}
+
+static const Eina_Value_Type *_ego_future_value_type_get(const Eina_Value *v) {
+    return eina_value_type_get(v);
+}
+
+static Eina_Bool _ego_future_value_copy(const Eina_Value *src, Eina_Value *dst) {
+    return eina_value_copy(src, dst);
+}
+*/
+import "C"
+
+import "unsafe"
+
+// FutureToChannel wraps an Eina_Future* in a Go channel. The channel carries
+// at most one *Value — the resolved result — and is closed once the future
+// settles. Callers receive the result with a plain channel receive:
+//
+//	v := <-FutureToChannel(ptr)
+//
+// If futurePtr is nil the channel is returned already closed (no value sent),
+// which callers should treat as a cancelled or errored future.
+//
+// Ownership: the returned *Value is heap-allocated and owned by the caller;
+// call v.Free() when done with it.
+func FutureToChannel(futurePtr unsafe.Pointer) <-chan *Value {
+	if futurePtr == nil {
+		ch := make(chan *Value, 1)
+		close(ch)
+		return ch
+	}
+	ch := make(chan *Value, 1)
+	id := CallbackRegister(ch)
+	C._ego_future_then((*C.Eina_Future)(futurePtr), C.uintptr_t(id))
+	return ch
+}
+
+// egoFutureResolved is called by the C trampoline _ego_future_cb when an
+// Eina_Future resolves. It copies the resolved Eina_Value to the heap (the
+// original is owned by the expiring future and will be freed by EFL after this
+// callback returns), sends the copy on the waiting channel, closes the channel,
+// and removes the callback map entry.
+//
+// If the resolved value has no type (e.g. EFL signalled an error with an empty
+// value) the channel is closed without sending, so callers receive the zero
+// value of *Value (nil).
+//
+//export egoFutureResolved
+func egoFutureResolved(data unsafe.Pointer, value *C.Eina_Value) {
+	id := uintptr(data)
+	ch := CallbackLookup[chan *Value](id)
+
+	// Always clean up the map entry and close the channel, even on the error
+	// path, so callers are never left blocked.
+	defer func() {
+		CallbackRemove(id)
+		if ch != nil {
+			close(ch)
+		}
+	}()
+
+	if ch == nil {
+		return
+	}
+
+	// An empty/error value has a nil type pointer; skip the copy in that case.
+	vtype := C._ego_future_value_type_get(value)
+	if vtype == nil {
+		return
+	}
+
+	cp := C._ego_future_value_new(vtype)
+	if cp == nil {
+		return
+	}
+
+	if C._ego_future_value_copy(value, cp) == 0 {
+		// Copy failed; free the partially-constructed allocation so we don't
+		// leak and send nothing on the channel.
+		C._ego_future_value_free(cp)
+		return
+	}
+
+	ch <- &Value{ptr: cp, owned: true}
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to