#include <assert.h>
+#include <ecore_private.h>
+
struct _Ecore_Thread_Data
{
Ecore_Thread_Promise_Cb func_blocking;
{
Eina_Promise_Owner owner_vtable;
Eina_Promise_Owner* eina_owner;
+ Eina_Promise promise_vtable;
+ Eina_Promise* eina_promise;
_Ecore_Thread_Data thread_callback_data;
+ int ref_count;
+ int then_count;
};
typedef struct _Ecore_Thread_Promise_Owner _Ecore_Thread_Promise_Owner;
+#define ECORE_PROMISE_GET_OWNER(p) (_Ecore_Thread_Promise_Owner*)((unsigned char*)p - offsetof(struct _Ecore_Thread_Promise_Owner, promise_vtable))
+
+static void _ecore_promise_ref_update(_Ecore_Thread_Promise_Owner* p)
+{
+ if(p->ref_count < 0)
+ {
+ ERR("Reference count is negative for promise %p\n", p);
+ }
+ if(!p->ref_count)
+ {
+ p->eina_promise->unref(p->eina_promise);
+ p->eina_promise = NULL;
+ p->eina_owner = NULL;
+ p->thread_callback_data.thread = NULL;
+ }
+}
+
+static void _ecore_promise_thread_release_ref(void* data, void* value EINA_UNUSED)
+{
+ _Ecore_Thread_Promise_Owner* p = data;
+ p->ref_count -= p->then_count;
+ _ecore_promise_ref_update(p);
+}
+
static void _ecore_promise_thread_end(void* data, Ecore_Thread* thread EINA_UNUSED)
{
_Ecore_Thread_Promise_Owner* p = data;
{
eina_promise_owner_default_manual_then_set(p->eina_owner, EINA_FALSE);
eina_promise_owner_default_call_then(p->eina_owner);
+ p->ref_count -= p->then_count;
+ _ecore_promise_ref_update(p);
}
else
{
eina_promise_owner_default_manual_then_set(p->eina_owner, EINA_FALSE);
+ eina_promise_then(p->eina_promise, &_ecore_promise_thread_release_ref,
+ (Eina_Promise_Error_Cb)&_ecore_promise_thread_release_ref, p);
}
}
eina_promise_owner_progress(promise->eina_owner, msg_data);
}
-static void _ecore_promise_cancel(void* data, Eina_Promise_Owner* promise EINA_UNUSED)
+static void _ecore_promise_cancel_cb(void* data, Eina_Promise_Owner* promise EINA_UNUSED)
{
_Ecore_Thread_Promise_Owner* priv = data;
(priv->thread_callback_data.func_cancel)(priv->thread_callback_data.data, &priv->owner_vtable,
}
static Eina_Promise* _ecore_thread_promise_owner_promise_get(_Ecore_Thread_Promise_Owner* promise)
{
- return promise->eina_owner->promise_get(promise->eina_owner);
+ return &promise->promise_vtable;
}
static void _ecore_thread_promise_owner_progress(_Ecore_Thread_Promise_Owner* promise, void* data)
{
ecore_thread_feedback(promise->thread_callback_data.thread, data);
}
+static void _ecore_thread_promise_owner_progress_notify(_Ecore_Thread_Promise_Owner* promise,
+ Eina_Promise_Progress_Notify_Cb progress_cb,
+ void* data, Eina_Promise_Free_Cb free_cb)
+{
+ promise->eina_owner->progress_notify(promise->eina_owner, progress_cb, data, free_cb);
+}
+
+static void _ecore_promise_then(Eina_Promise* promise, Eina_Promise_Cb callback,
+ Eina_Promise_Error_Cb error_cb, void* data)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ v->eina_promise->then(v->eina_promise, callback, error_cb, data);
+ if(v->then_count)
+ {
+ v->ref_count++;
+ }
+ v->then_count++;
+}
+static void* _ecore_promise_value_get(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ return v->eina_promise->value_get(v->eina_promise);
+}
+static Eina_Error _ecore_promise_error_get(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ return v->eina_promise->error_get(v->eina_promise);
+}
+static Eina_Bool _ecore_promise_pending_is(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ return v->eina_promise->pending_is(v->eina_promise);
+}
+static void _ecore_promise_progress_cb_add(Eina_Promise const* promise, Eina_Promise_Progress_Cb callback, void* data,
+ Eina_Promise_Free_Cb free_cb)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ v->eina_promise->progress_cb_add(v->eina_promise, callback, data, free_cb);
+}
+static void _ecore_promise_cancel(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ v->eina_promise->cancel(v->eina_promise);
+}
+static void _ecore_promise_ref(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ ++v->ref_count;
+}
+static void _ecore_promise_unref(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ --v->ref_count;
+}
+static void* _ecore_promise_buffer_get(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ return v->eina_promise->buffer_get(v->eina_promise);
+}
+static size_t _ecore_promise_value_size_get(Eina_Promise const* promise)
+{
+ _Ecore_Thread_Promise_Owner* v = ECORE_PROMISE_GET_OWNER(promise);
+ return v->eina_promise->value_size_get(v->eina_promise);
+}
Ecore_Thread* ecore_thread_promise_run(Ecore_Thread_Promise_Cb func_blocking,
Ecore_Thread_Promise_Cb func_cancel,
priv->owner_vtable.pending_is = EINA_FUNC_PROMISE_OWNER_PENDING_IS(&_ecore_promise_owner_pending_is);
priv->owner_vtable.cancelled_is = EINA_FUNC_PROMISE_OWNER_CANCELLED_IS(&_ecore_promise_owner_cancelled_is);
priv->owner_vtable.progress = EINA_FUNC_PROMISE_OWNER_PROGRESS(&_ecore_thread_promise_owner_progress);
-
+ priv->owner_vtable.progress_notify = EINA_FUNC_PROMISE_OWNER_PROGRESS_NOTIFY(&_ecore_thread_promise_owner_progress_notify);
+
+ priv->promise_vtable.then = EINA_FUNC_PROMISE_THEN(&_ecore_promise_then);
+ priv->promise_vtable.value_get = EINA_FUNC_PROMISE_VALUE_GET(&_ecore_promise_value_get);
+ priv->promise_vtable.error_get = EINA_FUNC_PROMISE_ERROR_GET(&_ecore_promise_error_get);
+ priv->promise_vtable.pending_is = EINA_FUNC_PROMISE_PENDING_IS(&_ecore_promise_pending_is);
+ priv->promise_vtable.progress_cb_add = EINA_FUNC_PROMISE_PROGRESS_CB_ADD(&_ecore_promise_progress_cb_add);
+ priv->promise_vtable.cancel = EINA_FUNC_PROMISE_CANCEL(&_ecore_promise_cancel);
+ priv->promise_vtable.ref = EINA_FUNC_PROMISE_REF(&_ecore_promise_ref);
+ priv->promise_vtable.unref = EINA_FUNC_PROMISE_UNREF(&_ecore_promise_unref);
+ priv->promise_vtable.value_size_get = EINA_FUNC_PROMISE_VALUE_SIZE_GET(&_ecore_promise_value_size_get);
+ priv->promise_vtable.buffer_get = EINA_FUNC_PROMISE_BUFFER_GET(&_ecore_promise_buffer_get);
+
priv->thread_callback_data.data = data;
priv->thread_callback_data.func_blocking = func_blocking;
priv->thread_callback_data.func_cancel = func_cancel;
eina_promise_owner_default_manual_then_set(priv->eina_owner, EINA_TRUE);
+
+ priv->eina_promise = priv->eina_owner->promise_get(priv->eina_owner);
+ priv->eina_promise->ref(priv->eina_promise);
+ priv->ref_count = 0;
+ priv->then_count = 0;
+
if(func_cancel)
- eina_promise_owner_default_cancel_cb_add(priv->eina_owner, &_ecore_promise_cancel, priv, NULL);
+ eina_promise_owner_default_cancel_cb_add(priv->eina_owner, &_ecore_promise_cancel_cb, priv, NULL);
priv->thread_callback_data.thread =
ecore_thread_feedback_run(&_ecore_promise_thread_blocking, &_ecore_promise_thread_notify,
&_ecore_promise_thread_end, &_ecore_promise_thread_cancel, priv,
EINA_FALSE);
if(promise)
- *promise = priv->eina_owner->promise_get(priv->eina_owner);
+ *promise = priv->eina_promise;
return priv->thread_callback_data.thread;
}
static int _eina_main_thread_count = 0;
#endif
static int _eina_log_dom = -1;
-void _eina_promise_init(void);
#ifdef ERR
#undef ERR
S(cpu);
S(thread_queue);
S(rbtree);
+ S(promise);
/* no model for now
S(model);
*/
S(cow),
S(cpu),
S(thread_queue),
- S(rbtree)
+ S(rbtree),
+ S(promise)
/* no model for now
S(model)
*/
}
}
- _eina_promise_init();
-
eina_cpu_count_internal();
eina_log_timing(_eina_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT);
#include <Eina.h>
+#include <eina_private.h>
+
#include <assert.h>
+static int _eina_promise_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_promise_log_dom, __VA_ARGS__)
+
typedef struct _Eina_Promise_Then_Cb _Eina_Promise_Then_Cb;
typedef struct _Eina_Promise_Progress_Cb _Eina_Promise_Progress_Cb;
typedef struct _Eina_Promise_Cancel_Cb _Eina_Promise_Cancel_Cb;
EINA_INLIST;
Eina_Promise_Progress_Cb callback;
+ Eina_Promise_Free_Cb free;
void* data;
};
} data;
};
+static void _eina_promise_free_progress_callback_node(void* node)
+{
+ _Eina_Promise_Progress_Cb *progress_cb = node;
+ if(progress_cb->free)
+ progress_cb->free(progress_cb->data);
+ free(progress_cb);
+}
+
+static void _eina_promise_free_progress_notify_callback_node(void* node)
+{
+ _Eina_Promise_Owner_Progress_Notify_Data *progress_notify_cb = node;
+ if(progress_notify_cb->free_cb)
+ progress_notify_cb->free_cb(progress_notify_cb->data);
+ free(progress_notify_cb);
+}
+
static void _eina_promise_finish(_Eina_Promise_Default_Owner* promise);
static void _eina_promise_ref(_Eina_Promise_Default* promise);
static void _eina_promise_unref(_Eina_Promise_Default* promise);
_eina_promise_then_calls(_Eina_Promise_Default_Owner* promise)
{
_Eina_Promise_Then_Cb* callback;
- Eina_Inlist* list2;
Eina_Bool error;
_eina_promise_ref(&promise->promise);
error = promise->promise.has_errored;
- EINA_INLIST_FOREACH_SAFE(promise->promise.then_callbacks, list2, callback)
+ EINA_INLIST_FREE(promise->promise.then_callbacks, callback)
{
+ promise->promise.then_callbacks = eina_inlist_remove(promise->promise.then_callbacks, EINA_INLIST_GET(callback));
if (error)
{
if (callback->error_cb)
{
(*callback->callback)(callback->data, &promise->value[0]);
}
+ free(callback);
_eina_promise_unref(&promise->promise);
}
_eina_promise_unref(&promise->promise);
_eina_promise_cancel_calls(_Eina_Promise_Default_Owner* promise, Eina_Bool call_cancel EINA_UNUSED)
{
_Eina_Promise_Cancel_Cb* callback;
- Eina_Inlist* list2;
- EINA_INLIST_FOREACH_SAFE(promise->promise.cancel_callbacks, list2, callback)
+ EINA_INLIST_FREE(promise->promise.cancel_callbacks, callback)
{
- if (callback->callback)
- {
- (*callback->callback)(callback->data, (Eina_Promise_Owner*)promise);
- }
+ promise->promise.cancel_callbacks = eina_inlist_remove(promise->promise.cancel_callbacks, EINA_INLIST_GET(callback));
+ if (callback->callback)
+ {
+ (*callback->callback)(callback->data, (Eina_Promise_Owner*)promise);
+ }
+ free(callback);
}
if (!promise->promise.is_manual_then)
static void
_eina_promise_del(_Eina_Promise_Default_Owner* promise)
{
- if (promise->promise.has_finished)
- {
- if (promise->promise.value_free_cb)
- promise->promise.value_free_cb((void*)&promise->value[0]);
- }
- else
+ if (!promise->promise.has_finished)
{
- _eina_promise_cancel_calls(promise, EINA_TRUE);
+ ERR("Promise is being deleted, despite not being finished yet. This will cause intermitent crashes");
}
+
+ if (promise->promise.value_free_cb)
+ promise->promise.value_free_cb((void*)&promise->value[0]);
+
+ _eina_promise_free_callback_list(&promise->promise.progress_callbacks,
+ &_eina_promise_free_progress_callback_node);
+ _eina_promise_free_callback_list(&promise->promise.progress_notify_callbacks,
+ &_eina_promise_free_progress_notify_callback_node);
+ free(promise);
}
static void *
if (!promise->promise.is_first_then)
{
_eina_promise_ref(p);
- promise->promise.is_first_then = EINA_FALSE;
}
+ else
+ promise->promise.is_first_then = EINA_FALSE;
if (promise->promise.has_finished)
{
_eina_promise_then_calls(promise);
{
_eina_promise_then_calls(promise);
}
+ if(promise->promise.ref == 0)
+ {
+ _eina_promise_del(promise);
+ }
}
static Eina_Error
}
static void
-_eina_promise_progress_cb_add(_Eina_Promise_Default* promise, Eina_Promise_Progress_Cb callback, void* data)
+_eina_promise_progress_cb_add(_Eina_Promise_Default* promise, Eina_Promise_Progress_Cb callback, void* data,
+ Eina_Promise_Free_Cb free_cb)
{
_Eina_Promise_Progress_Cb* cb;
_Eina_Promise_Owner_Progress_Notify_Data* notify_data;
cb = malloc(sizeof(struct _Eina_Promise_Progress_Cb));
cb->callback = callback;
cb->data = data;
+ cb->free = free_cb;
promise->progress_callbacks = eina_inlist_append(promise->progress_callbacks, EINA_INLIST_GET(cb));
EINA_INLIST_FOREACH(owner->promise.progress_notify_callbacks, notify_data)
{
(*notify_data->callback)(notify_data->data, &owner->owner_vtable);
}
- _eina_promise_free_callback_list(&owner->promise.progress_notify_callbacks, &free);
+ _eina_promise_free_callback_list(&owner->promise.progress_notify_callbacks,
+ &_eina_promise_free_progress_notify_callback_node);
}
static void
EINA_INLIST_FOREACH_SAFE(promise->promise.progress_callbacks, list2, callback)
{
- (*callback->callback)(callback->data, data);
+ if(callback->callback)
+ (*callback->callback)(callback->data, data);
}
}
}
EAPI Eina_Error EINA_ERROR_PROMISE_NO_NOTIFY;
+EAPI Eina_Error EINA_ERROR_PROMISE_CANCEL;
static void
_eina_promise_progress_notify_failed(void* data)
}
EAPI void
-eina_promise_progress_cb_add(Eina_Promise* promise, Eina_Promise_Progress_Cb callback, void* data)
+eina_promise_progress_cb_add(Eina_Promise* promise, Eina_Promise_Progress_Cb callback, void* data,
+ Eina_Promise_Free_Cb free_cb)
{
- promise->progress_cb_add(promise, callback, data);
+ promise->progress_cb_add(promise, callback, data, free_cb);
}
EAPI void
}
static const char EINA_ERROR_PROMISE_NO_NOTIFY_STR[] = "Out of memory";
+static const char EINA_ERROR_PROMISE_CANCEL_STR[] = "Promise cancelled";
-void _eina_promise_init()
+Eina_Bool eina_promise_init()
{
EINA_ERROR_PROMISE_NO_NOTIFY = eina_error_msg_static_register(EINA_ERROR_PROMISE_NO_NOTIFY_STR);
+ EINA_ERROR_PROMISE_CANCEL = eina_error_msg_static_register(EINA_ERROR_PROMISE_CANCEL_STR);
+
+ _eina_promise_log_dom = eina_log_domain_register("eina_promise",
+ EINA_LOG_COLOR_DEFAULT);
+ if (_eina_promise_log_dom < 0)
+ {
+ EINA_LOG_ERR("Could not register log domain: eina_promise");
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+Eina_Bool eina_promise_shutdown()
+{
+ eina_log_domain_unregister(_eina_promise_log_dom);
+ _eina_promise_log_dom = -1;
+ return EINA_TRUE;
}
/*
* @brief Function callback type for promise's progress add function override
*/
-typedef void(*Eina_Promise_Progress_Cb_Add_Cb)(Eina_Promise* promise, Eina_Promise_Progress_Cb callback, void* data);
+typedef void(*Eina_Promise_Progress_Cb_Add_Cb)(Eina_Promise* promise, Eina_Promise_Progress_Cb callback, void* data
+ , Eina_Promise_Free_Cb free_cb);
#define EINA_FUNC_PROMISE_PROGRESS_CB_ADD(Function) ((Eina_Promise_Progress_Cb_Add_Cb)Function)
* @param progress The callback to be called when progress is made
* @param data The private data that will be passed to the progress callback
*/
-EAPI void eina_promise_progress_cb_add(Eina_Promise* promise, Eina_Promise_Progress_Cb progress, void* data);
+EAPI void eina_promise_progress_cb_add(Eina_Promise* promise, Eina_Promise_Progress_Cb progress, void* data,
+ Eina_Promise_Free_Cb free_cb);
/*
* @brief Increments the reference count for the Eina_Promise
*/
EAPI extern Eina_Error EINA_ERROR_PROMISE_NO_NOTIFY;
+/**
+ * @var EINA_ERROR_PROMISE_CANCEL
+ *
+ * @brief The error identifier corresponding to when a promise was
+ * cancelled before the callback can be called
+ */
+EAPI extern Eina_Error EINA_ERROR_PROMISE_CANCEL;
+
/*
* @internal
*/
ecore_thread_promise_run(&promise_progress_thread, NULL, NULL, 0, &promise);
- eina_promise_progress_cb_add(promise, &_progress_callback, NULL);
+ eina_promise_progress_cb_add(promise, &_progress_callback, NULL, NULL);
ecore_main_loop_begin();
owner = eina_promise_default_add(0);
promise = eina_promise_owner_promise_get(owner);
- eina_promise_progress_cb_add(promise, &progress_callback, &progress_ran);
+ eina_promise_progress_cb_add(promise, &progress_callback, &progress_ran, NULL);
eina_promise_owner_progress(owner, &i);
eina_promise_owner_progress_notify(owner, &progress_notify, &progress_notify_ran, NULL);
promise = eina_promise_owner_promise_get(owner);
- eina_promise_progress_cb_add(promise, &progress_callback, NULL); // never run
- eina_promise_progress_cb_add(promise, &progress_callback, NULL); // never run
+ eina_promise_progress_cb_add(promise, &progress_callback, NULL, NULL); // never run
+ eina_promise_progress_cb_add(promise, &progress_callback, NULL, NULL); // never run
ck_assert(progress_notify_ran);
&_eina_promise_progress_notify_error, &progress_notify_ran);
promise = eina_promise_owner_promise_get(owner);
- eina_promise_progress_cb_add(promise, &progress_callback, NULL); // never run
- eina_promise_progress_cb_add(promise, &progress_callback, NULL); // never run
+ eina_promise_progress_cb_add(promise, &progress_callback, NULL, NULL); // never run
+ eina_promise_progress_cb_add(promise, &progress_callback, NULL, NULL); // never run
ck_assert(progress_notify_ran);
}
END_TEST
+START_TEST(eina_test_promise_ignored)
+{
+ Eina_Promise_Owner* owner;
+ Eina_Promise* promise;
+
+ eina_init();
+
+ owner = eina_promise_default_add(0);
+ promise = eina_promise_owner_promise_get(owner);
+ eina_promise_unref(promise);
+ eina_promise_owner_value_set(owner, NULL, NULL);
+
+ eina_shutdown();
+}
+END_TEST
+
void
eina_test_promise(TCase *tc)
{
tcase_add_test(tc, eina_test_promise_progress_notify1);
tcase_add_test(tc, eina_test_promise_progress_notify2);
tcase_add_test(tc, eina_test_promise_progress_notify3);
+ tcase_add_test(tc, eina_test_promise_ignored);
}