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.