eina: Fix memory leaks in promise

This commit is contained in:
Felipe Magno de Almeida 2016-05-06 16:32:10 -03:00
parent ba1e70278e
commit 6d43adaaf4
6 changed files with 242 additions and 38 deletions

View File

@ -7,6 +7,8 @@
#include <assert.h>
#include <ecore_private.h>
struct _Ecore_Thread_Data
{
Ecore_Thread_Promise_Cb func_blocking;
@ -20,10 +22,38 @@ struct _Ecore_Thread_Promise_Owner
{
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;
@ -31,10 +61,14 @@ static void _ecore_promise_thread_end(void* data, Ecore_Thread* thread EINA_UNUS
{
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);
}
}
@ -51,7 +85,7 @@ static void _ecore_promise_thread_notify(void* data, Ecore_Thread* thread EINA_U
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,
@ -93,12 +127,76 @@ static Eina_Bool _ecore_promise_owner_cancelled_is(_Ecore_Thread_Promise_Owner c
}
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,
@ -120,18 +218,36 @@ Ecore_Thread* ecore_thread_promise_run(Ecore_Thread_Promise_Cb func_blocking,
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;
}

View File

@ -87,7 +87,6 @@ static int _eina_main_count = 0;
static int _eina_main_thread_count = 0;
#endif
static int _eina_log_dom = -1;
void _eina_promise_init(void);
#ifdef ERR
#undef ERR
@ -155,6 +154,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
S(cpu);
S(thread_queue);
S(rbtree);
S(promise);
/* no model for now
S(model);
*/
@ -201,7 +201,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
S(cow),
S(cpu),
S(thread_queue),
S(rbtree)
S(rbtree),
S(promise)
/* no model for now
S(model)
*/
@ -300,8 +301,6 @@ eina_init(void)
}
}
_eina_promise_init();
eina_cpu_count_internal();
eina_log_timing(_eina_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT);

View File

@ -4,8 +4,17 @@
#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;
@ -29,6 +38,7 @@ struct _Eina_Promise_Progress_Cb
EINA_INLIST;
Eina_Promise_Progress_Cb callback;
Eina_Promise_Free_Cb free;
void* data;
};
@ -94,6 +104,22 @@ struct _Eina_Promise_Iterator
} 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);
@ -119,14 +145,14 @@ static void
_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)
@ -136,6 +162,7 @@ _eina_promise_then_calls(_Eina_Promise_Default_Owner* promise)
{
(*callback->callback)(callback->data, &promise->value[0]);
}
free(callback);
_eina_promise_unref(&promise->promise);
}
_eina_promise_unref(&promise->promise);
@ -145,14 +172,15 @@ static void
_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)
@ -164,15 +192,19 @@ _eina_promise_cancel_calls(_Eina_Promise_Default_Owner* promise, Eina_Bool call_
static void
_eina_promise_del(_Eina_Promise_Default_Owner* promise)
{
if (promise->promise.has_finished)
if (!promise->promise.has_finished)
{
if (promise->promise.value_free_cb)
promise->promise.value_free_cb((void*)&promise->value[0]);
}
else
{
_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 *
@ -234,8 +266,9 @@ _eina_promise_then(_Eina_Promise_Default* p, Eina_Promise_Cb callback,
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);
@ -259,6 +292,10 @@ _eina_promise_finish(_Eina_Promise_Default_Owner* promise)
{
_eina_promise_then_calls(promise);
}
if(promise->promise.ref == 0)
{
_eina_promise_del(promise);
}
}
static Eina_Error
@ -293,7 +330,8 @@ _eina_promise_owner_cancelled_is(_Eina_Promise_Default_Owner const* promise)
}
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;
@ -302,13 +340,15 @@ _eina_promise_progress_cb_add(_Eina_Promise_Default* promise, Eina_Promise_Progr
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
@ -393,7 +433,8 @@ _eina_promise_owner_progress(_Eina_Promise_Default_Owner* promise, void* data)
EINA_INLIST_FOREACH_SAFE(promise->promise.progress_callbacks, list2, callback)
{
(*callback->callback)(callback->data, data);
if(callback->callback)
(*callback->callback)(callback->data, data);
}
}
@ -605,6 +646,7 @@ _eina_promise_progress_notify_fulfilled(void* data, Eina_Promise_Owner* p EINA_U
}
EAPI Eina_Error EINA_ERROR_PROMISE_NO_NOTIFY;
EAPI Eina_Error EINA_ERROR_PROMISE_CANCEL;
static void
_eina_promise_progress_notify_failed(void* data)
@ -666,9 +708,10 @@ eina_promise_pending_is(Eina_Promise const* promise)
}
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
@ -739,8 +782,27 @@ eina_promise_owner_progress_notify(Eina_Promise_Owner* promise, Eina_Promise_Pro
}
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;
}

View File

@ -71,7 +71,8 @@ typedef Eina_Bool(*Eina_Promise_Pending_Is_Cb)(Eina_Promise const* promise);
/*
* @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)
@ -338,7 +339,8 @@ EAPI Eina_Bool eina_promise_pending_is(Eina_Promise const* promise);
* @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
@ -483,6 +485,14 @@ EAPI void eina_promise_owner_default_call_then(Eina_Promise_Owner* 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
*/

View File

@ -378,7 +378,7 @@ START_TEST(ecore_test_promise_progress_promise)
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();

View File

@ -226,7 +226,7 @@ START_TEST(eina_test_promise_progress)
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);
@ -254,8 +254,8 @@ START_TEST(eina_test_promise_progress_notify1)
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);
@ -311,8 +311,8 @@ START_TEST(eina_test_promise_progress_notify3)
&_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);
@ -320,6 +320,22 @@ START_TEST(eina_test_promise_progress_notify3)
}
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)
{
@ -333,4 +349,5 @@ 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);
}