From 33c00de365ab8ed2adebd86e5e3dc6091afc0fa9 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 7 Dec 2018 12:15:16 +0100 Subject: [PATCH] eo: make efl_future_then have a data pointer in addition of the object pointer. Summary: In the case when you have multiple future in flight related to one object, you couldn't use the previous version of efl_future_then. Now all function calls take a void* pointer that allow multiple future to have their private data request data accessible in all the callback. This should not break released API as Eo.h is not released yet and so was efl_future_Eina_FutureXXX_then. Depends on D7332 Reviewers: felipealmeida, segfaultxavi, vitor.sousa, SanghyeonLee, bu5hm4n Reviewed By: segfaultxavi Subscribers: #reviewers, #committers Tags: #efl Maniphest Tasks: T7472 Differential Revision: https://phab.enlightenment.org/D7379 --- src/lib/ecore/efl_io_copier.c | 14 +++++++------- src/lib/ecore_con/efl_net_dialer_http.c | 8 ++++---- src/lib/ecore_con/efl_net_dialer_ssl.c | 6 +++--- src/lib/ecore_con/efl_net_dialer_tcp.c | 6 +++--- src/lib/ecore_con/efl_net_dialer_udp.c | 6 +++--- src/lib/ecore_con/efl_net_dialer_unix.c | 6 +++--- src/lib/ecore_con/efl_net_dialer_websocket.c | 12 ++++++------ src/lib/ecore_con/efl_net_server_windows.c | 6 +++--- src/lib/eina/eina_promise_private.h | 18 +++++++++--------- src/lib/eo/Eo.h | 19 +++++++++++++------ src/lib/eo/eo_base_class.c | 6 +++--- src/tests/ecore/efl_app_test_promise.c | 14 +++++++------- 12 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/lib/ecore/efl_io_copier.c b/src/lib/ecore/efl_io_copier.c index 28f5ab68aa..a03168807c 100644 --- a/src/lib/ecore/efl_io_copier.c +++ b/src/lib/ecore/efl_io_copier.c @@ -72,7 +72,7 @@ static void _efl_io_copier_read(Eo *o, Efl_Io_Copier_Data *pd); while (0) static Eina_Value -_efl_io_copier_timeout_inactivity_cb(Eo *o, const Eina_Value v) +_efl_io_copier_timeout_inactivity_cb(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Eina_Error err = ETIMEDOUT; efl_event_callback_call(o, EFL_IO_COPIER_EVENT_ERROR, &err); @@ -86,12 +86,12 @@ _efl_io_copier_timeout_inactivity_reschedule(Eo *o, Efl_Io_Copier_Data *pd) if (pd->timeout_inactivity <= 0.0) return; efl_future_then(o, efl_loop_timeout(efl_loop_get(o), pd->timeout_inactivity), - .success = _efl_io_copier_timeout_inactivity_cb, - .storage = &pd->inactivity_timer); + .success = _efl_io_copier_timeout_inactivity_cb, + .storage = &pd->inactivity_timer); } static Eina_Value -_efl_io_copier_job(Eo *o, const Eina_Value v) +_efl_io_copier_job(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Io_Copier_Data *pd = efl_data_scope_get(o, MY_CLASS); uint64_t old_read = pd->progress.read; @@ -138,14 +138,14 @@ _efl_io_copier_job_schedule(Eo *o, Efl_Io_Copier_Data *pd) { Eina_Value v = EINA_VALUE_EMPTY; - v = _efl_io_copier_job(o, v); + v = _efl_io_copier_job(o, NULL, v); eina_value_flush(&v); } else { efl_future_then(o, efl_loop_job(efl_loop_get(o)), - .success = _efl_io_copier_job, - .storage = &pd->job); + .success = _efl_io_copier_job, + .storage = &pd->job); } } diff --git a/src/lib/ecore_con/efl_net_dialer_http.c b/src/lib/ecore_con/efl_net_dialer_http.c index bc004ba264..8bfe967c32 100644 --- a/src/lib/ecore_con/efl_net_dialer_http.c +++ b/src/lib/ecore_con/efl_net_dialer_http.c @@ -1717,7 +1717,7 @@ _efl_net_dialer_http_efl_io_writer_can_write_set(Eo *o, Efl_Net_Dialer_Http_Data efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL); } -static Eina_Value _efl_net_dialer_http_pending_close(Eo *o, Eina_Value value); +static Eina_Value _efl_net_dialer_http_pending_close(Eo *o, void *data, Eina_Value value); EOLIAN static Eina_Error _efl_net_dialer_http_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Http_Data *pd) @@ -1733,8 +1733,8 @@ _efl_net_dialer_http_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Http_Data *pd) if ((!pd->pending_close) && (pd->easy)) { efl_future_then(o, efl_loop_job(efl_loop_get(o)), - .success = _efl_net_dialer_http_pending_close, - .storage = &pd->pending_close); + .success = _efl_net_dialer_http_pending_close, + .storage = &pd->pending_close); DBG("dialer=%p closed from CURL callback, schedule close job=%p", o, pd->pending_close); } return 0; @@ -1766,7 +1766,7 @@ _efl_net_dialer_http_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Http_Data *pd) } static Eina_Value -_efl_net_dialer_http_pending_close(Eo *o, const Eina_Value value EINA_UNUSED) +_efl_net_dialer_http_pending_close(Eo *o, void *data EINA_UNUSED, const Eina_Value value EINA_UNUSED) { Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS); diff --git a/src/lib/ecore_con/efl_net_dialer_ssl.c b/src/lib/ecore_con/efl_net_dialer_ssl.c index d3e83f69c0..78b587973a 100644 --- a/src/lib/ecore_con/efl_net_dialer_ssl.c +++ b/src/lib/ecore_con/efl_net_dialer_ssl.c @@ -128,7 +128,7 @@ _efl_net_dialer_ssl_ssl_context_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_ } static Eina_Value -_efl_net_dialer_ssl_connect_timeout(Eo *o, const Eina_Value v) +_efl_net_dialer_ssl_connect_timeout(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Eina_Error err = ETIMEDOUT; @@ -143,8 +143,8 @@ static void _timeout_schedule(Eo *o, Efl_Net_Dialer_Ssl_Data *pd, double timeout) { efl_future_then(o, efl_loop_timeout(efl_loop_get(o), timeout), - .success = _efl_net_dialer_ssl_connect_timeout, - .storage = &pd->connect_timeout); + .success = _efl_net_dialer_ssl_connect_timeout, + .storage = &pd->connect_timeout); } EOLIAN static Eina_Error diff --git a/src/lib/ecore_con/efl_net_dialer_tcp.c b/src/lib/ecore_con/efl_net_dialer_tcp.c index 57f5cf93cd..9a7a795faa 100644 --- a/src/lib/ecore_con/efl_net_dialer_tcp.c +++ b/src/lib/ecore_con/efl_net_dialer_tcp.c @@ -90,7 +90,7 @@ _efl_net_dialer_tcp_efl_object_destructor(Eo *o, Efl_Net_Dialer_Tcp_Data *pd) } static Eina_Value -_efl_net_dialer_tcp_connect_timeout(Eo *o, const Eina_Value v) +_efl_net_dialer_tcp_connect_timeout(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Net_Dialer_Tcp_Data *pd = efl_data_scope_get(o, MY_CLASS); Eina_Error err = ETIMEDOUT; @@ -108,8 +108,8 @@ static void _timeout_schedule(Eo *o, Efl_Net_Dialer_Tcp_Data *pd) { efl_future_then(o, efl_loop_timeout(efl_loop_get(o), pd->timeout_dial), - .success = _efl_net_dialer_tcp_connect_timeout, - .storage = &pd->connect.timeout); + .success = _efl_net_dialer_tcp_connect_timeout, + .storage = &pd->connect.timeout); } static void diff --git a/src/lib/ecore_con/efl_net_dialer_udp.c b/src/lib/ecore_con/efl_net_dialer_udp.c index f3e7ef2166..351d077909 100644 --- a/src/lib/ecore_con/efl_net_dialer_udp.c +++ b/src/lib/ecore_con/efl_net_dialer_udp.c @@ -82,7 +82,7 @@ _efl_net_dialer_udp_efl_object_destructor(Eo *o, Efl_Net_Dialer_Udp_Data *pd) } static Eina_Value -_efl_net_dialer_udp_resolver_timeout(Eo *o, const Eina_Value v) +_efl_net_dialer_udp_resolver_timeout(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Net_Dialer_Udp_Data *pd = efl_data_scope_get(o, MY_CLASS); Eina_Error err = ETIMEDOUT; @@ -104,8 +104,8 @@ static void _timeout_schedule(Eo *o, Efl_Net_Dialer_Udp_Data *pd) { efl_future_then(o, efl_loop_timeout(efl_loop_get(o), pd->timeout_dial), - .success = _efl_net_dialer_udp_resolver_timeout, - .storage = &pd->resolver.timeout); + .success = _efl_net_dialer_udp_resolver_timeout, + .storage = &pd->resolver.timeout); } static Eina_Error diff --git a/src/lib/ecore_con/efl_net_dialer_unix.c b/src/lib/ecore_con/efl_net_dialer_unix.c index d644d5d629..95e2b06457 100644 --- a/src/lib/ecore_con/efl_net_dialer_unix.c +++ b/src/lib/ecore_con/efl_net_dialer_unix.c @@ -71,7 +71,7 @@ _efl_net_dialer_unix_efl_object_destructor(Eo *o, Efl_Net_Dialer_Unix_Data *pd) } static Eina_Value -_efl_net_dialer_unix_connect_timeout(Eo *o, const Eina_Value v) +_efl_net_dialer_unix_connect_timeout(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Net_Dialer_Unix_Data *pd = efl_data_scope_get(o, MY_CLASS); Eina_Error err = ETIMEDOUT; @@ -130,8 +130,8 @@ static void _timeout_schedule(Eo *o, Efl_Net_Dialer_Unix_Data *pd) { efl_future_then(o, efl_loop_timeout(efl_loop_get(o), pd->timeout_dial), - .success = _efl_net_dialer_unix_connect_timeout, - .storage = &pd->connect.timeout); + .success = _efl_net_dialer_unix_connect_timeout, + .storage = &pd->connect.timeout); } EOLIAN static Eina_Error diff --git a/src/lib/ecore_con/efl_net_dialer_websocket.c b/src/lib/ecore_con/efl_net_dialer_websocket.c index 3c5339d3a2..30545f23c9 100644 --- a/src/lib/ecore_con/efl_net_dialer_websocket.c +++ b/src/lib/ecore_con/efl_net_dialer_websocket.c @@ -735,7 +735,7 @@ _efl_net_dialer_websocket_job_receive(Eo *o, Efl_Net_Dialer_Websocket_Data *pd) } static Eina_Value -_efl_net_dialer_websocket_job(Eo *o, const Eina_Value v) +_efl_net_dialer_websocket_job(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Net_Dialer_Websocket_Data *pd = efl_data_scope_get(o, MY_CLASS); @@ -765,8 +765,8 @@ _efl_net_dialer_websocket_job_schedule(Eo *o, Efl_Net_Dialer_Websocket_Data *pd) if (!loop) return; efl_future_then(o, efl_loop_job(loop), - .success = _efl_net_dialer_websocket_job, - .storage = &pd->job); + .success = _efl_net_dialer_websocket_job, + .storage = &pd->job); } static void @@ -1486,7 +1486,7 @@ _efl_net_dialer_websocket_binary_send(Eo *o, Efl_Net_Dialer_Websocket_Data *pd, } static Eina_Value -_efl_net_dialer_websocket_close_request_timeout(Eo *o, const Eina_Value v) +_efl_net_dialer_websocket_close_request_timeout(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { DBG("server did not close the TCP socket, timeout"); efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL); @@ -1505,8 +1505,8 @@ _efl_net_dialer_websocket_close_request(Eo *o, Efl_Net_Dialer_Websocket_Data *pd eina_future_cancel(pd->close_timeout); efl_future_then(o, efl_loop_timeout(efl_loop_get(o), 2.0), - .success = _efl_net_dialer_websocket_close_request_timeout, - .storage = &pd->close_timeout); + .success = _efl_net_dialer_websocket_close_request_timeout, + .storage = &pd->close_timeout); efl_io_writer_can_write_set(o, EINA_FALSE); diff --git a/src/lib/ecore_con/efl_net_server_windows.c b/src/lib/ecore_con/efl_net_server_windows.c index 8859036ad2..68e7fe9906 100644 --- a/src/lib/ecore_con/efl_net_server_windows.c +++ b/src/lib/ecore_con/efl_net_server_windows.c @@ -312,7 +312,7 @@ _efl_net_server_windows_efl_net_server_address_get(const Eo *o EINA_UNUSED, Efl_ } static Eina_Value -_efl_net_server_windows_pending_announce_job(Eo *o, const Eina_Value v) +_efl_net_server_windows_pending_announce_job(Eo *o, void *data EINA_UNUSED, const Eina_Value v) { Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS); Eo *client; @@ -339,8 +339,8 @@ _efl_net_server_windows_pending_announce_job_schedule(Eo *o, Efl_Net_Server_Wind loop = efl_loop_get(o); if (!loop) return; efl_future_then(o, efl_loop_job(loop), - .success = _efl_net_server_windows_pending_announce_job, - .storage = &pd->pending_announcer_job); + .success = _efl_net_server_windows_pending_announce_job, + .storage = &pd->pending_announcer_job); } EOLIAN static void diff --git a/src/lib/eina/eina_promise_private.h b/src/lib/eina/eina_promise_private.h index 31375b5794..1aeb2e7e6e 100644 --- a/src/lib/eina/eina_promise_private.h +++ b/src/lib/eina/eina_promise_private.h @@ -1,38 +1,38 @@ #ifndef __EINA_PROMISE_PRIVATE_H__ #define __EINA_PROMISE_PRIVATE_H__ -#define ERROR_DISPATCH(_cbs, _ret, _value, _data) \ +#define ERROR_DISPATCH(_cbs, _ret, _value, ...) \ do { \ Eina_Error ERROR_DISPATCH__err; \ if (!(_cbs)->error) (_ret) = (_value); \ else \ { \ eina_value_get(&(_value), &ERROR_DISPATCH__err); \ - (_ret) = (_cbs)->error((_data), ERROR_DISPATCH__err); \ + (_ret) = (_cbs)->error(__VA_ARGS__, ERROR_DISPATCH__err); \ } \ } while (0) -#define EASY_FUTURE_DISPATCH(_ret, _value, _dead_future, _cbs, _data) \ +#define EASY_FUTURE_DISPATCH(_ret, _value, _dead_future, _cbs, ...) \ do { \ - if ((_value).type == EINA_VALUE_TYPE_ERROR) ERROR_DISPATCH((_cbs), (_ret), (_value), (_data)); \ + if ((_value).type == EINA_VALUE_TYPE_ERROR) ERROR_DISPATCH((_cbs), (_ret), (_value), __VA_ARGS__); \ else \ { \ if ((!(_cbs)->success_type) || ((_cbs)->success_type && (_value).type == (_cbs)->success_type)) \ { \ if (!(_cbs)->success) (_ret) = (_value); /* pass thru */ \ - else (_ret) = (_cbs)->success((_data), (_value)); \ + else (_ret) = (_cbs)->success(__VA_ARGS__, (_value)); \ } \ else \ { \ Eina_Value EASY_FUTURE_DISPATCH__err = EINA_VALUE_EMPTY; \ - ERR("Future %p, success cb: %p data: %p, expected success_type %p (%s), got %p (%s)", \ - _dead_future, (_cbs)->success, (_data), \ + ERR("Future %p, success cb: %p, expected success_type %p (%s), got %p (%s)", \ + _dead_future, (_cbs)->success, \ (_cbs)->success_type, eina_value_type_name_get((_cbs)->success_type), \ (_value).type, (_value).type ? eina_value_type_name_get((_value).type) : NULL); \ if (eina_value_setup(&EASY_FUTURE_DISPATCH__err, EINA_VALUE_TYPE_ERROR)) eina_value_set(&EASY_FUTURE_DISPATCH__err, EINVAL); \ - ERROR_DISPATCH((_cbs), (_ret), EASY_FUTURE_DISPATCH__err, (_data)); \ + ERROR_DISPATCH((_cbs), (_ret), EASY_FUTURE_DISPATCH__err, __VA_ARGS__); \ } \ } \ - if ((_cbs)->free) (_cbs)->free((_data), _dead_future); \ + if ((_cbs)->free) (_cbs)->free(__VA_ARGS__, _dead_future); \ } while(0) #endif diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h index 8cd9f63d07..e953ac2709 100644 --- a/src/lib/eo/Eo.h +++ b/src/lib/eo/Eo.h @@ -379,7 +379,7 @@ typedef struct _Efl_Future_Cb_Desc { * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). */ - Eina_Value (*success)(Eo *o, const Eina_Value value); + Eina_Value (*success)(Eo *o, void *data, const Eina_Value value); /** * Called on error (value.type is @c EINA_VALUE_TYPE_ERROR). * @@ -417,7 +417,7 @@ typedef struct _Efl_Future_Cb_Desc { * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). */ - Eina_Value (*error)(Eo *o, Eina_Error error); + Eina_Value (*error)(Eo *o, void *data, Eina_Error error); /** * Called on @b all situations to notify future destruction. * @@ -431,7 +431,7 @@ typedef struct _Efl_Future_Cb_Desc { * @param o The object used to create the link in efl_future_cb_from_desc() or efl_future_chain_array(). * @param dead_future The future that's been freed. */ - void (*free)(Eo *o, const Eina_Future *dead_future); + void (*free)(Eo *o, void *data, const Eina_Future *dead_future); /** * If provided, then @c success will only be called if the value type matches the given pointer. * @@ -439,6 +439,13 @@ typedef struct _Efl_Future_Cb_Desc { * then it will be propagated to the next future in the chain. */ const Eina_Value_Type *success_type; + /** + * Context data given to every callback. + * + * This must be freed @b only by @c free callback as it's called from every case, + * otherwise it may lead to memory leaks. + */ + const void *data; /** * This is used by Eo to cancel pending futures in case * an Eo object is deleted. It can be @c NULL. @@ -476,7 +483,7 @@ typedef struct _Efl_Future_Cb_Desc { * } * * static Eina_Value - * _file_ok(Eo *o EINA_UNUSED, const Eina_Value value) + * _file_ok(Eo *o EINA_UNUSED, void *data EINA_UNUSED, const Eina_Value value) * { * const char *data; * //There's no need to check the value type since EO infra already has done so. @@ -487,7 +494,7 @@ typedef struct _Efl_Future_Cb_Desc { * } * * static Eina_Value - * _file_err(Eo *o EINA_UNUSED, Eina_Error error) + * _file_err(Eo *o EINA_UNUSED, void *data EINA_UNUSED, Eina_Error error) * { * //In case the downloader is deleted before the future is resolved, the future will be canceled thus this callback will be called. * fprintf(stderr, "Could not download the file. Reason: %s\n", eina_error_msg_get(error)); @@ -495,7 +502,7 @@ typedef struct _Efl_Future_Cb_Desc { * } * * static void - * _downlader_free(Eo *o, const Eina_Future *dead_future EINA_UNUSED) + * _downlader_free(Eo *o, void *data EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED) * { * Ecore_Timer *t = efl_key_data_get(o, "timer"); * //The download finished before the timer expired. Cancel it... diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c index 928811abcb..3ca8591b41 100644 --- a/src/lib/eo/eo_base_class.c +++ b/src/lib/eo/eo_base_class.c @@ -2080,7 +2080,7 @@ _efl_future_cb(void *data, const Eina_Value value, const Eina_Future *dead_futur pd->pending_futures = eina_inlist_remove(pd->pending_futures, EINA_INLIST_GET(pending)); efl_ref(o); - EASY_FUTURE_DISPATCH(ret, value, dead_future, &pending->desc, (void*) o); + EASY_FUTURE_DISPATCH(ret, value, dead_future, &pending->desc, (void*) o, (void*) pending->desc.data); efl_unref(o); _efl_pending_future_free(pending); @@ -2147,10 +2147,10 @@ efl_future_chain_array(Eo *obj, { if (descs[i].error) { - Eina_Value r = descs[i].error(obj, ENOMEM); + Eina_Value r = descs[i].error(obj, (void*) descs[i].data, ENOMEM); eina_value_flush(&r); } - if (descs[i].free) descs[i].free(obj, NULL); + if (descs[i].free) descs[i].free(obj, (void*) descs[i].data, NULL); } return NULL; } diff --git a/src/tests/ecore/efl_app_test_promise.c b/src/tests/ecore/efl_app_test_promise.c index fde88793b4..e68f74d60a 100644 --- a/src/tests/ecore/efl_app_test_promise.c +++ b/src/tests/ecore/efl_app_test_promise.c @@ -808,7 +808,7 @@ EFL_START_TEST(efl_test_promise_future_race) EFL_END_TEST static Eina_Value -_eo_future1_ok(Eo *eo EINA_UNUSED, const Eina_Value v) +_eo_future1_ok(Eo *eo EINA_UNUSED, void *data EINA_UNUSED, const Eina_Value v) { const char *number; @@ -819,14 +819,14 @@ _eo_future1_ok(Eo *eo EINA_UNUSED, const Eina_Value v) } static Eina_Value -_eo_future1_err(Eo *eo EINA_UNUSED, Eina_Error err EINA_UNUSED) +_eo_future1_err(Eo *eo EINA_UNUSED, void *data EINA_UNUSED, Eina_Error err EINA_UNUSED) { //Should not happen fail_if(EINA_TRUE); } static Eina_Value -_eo_future2_ok(Eo *eo EINA_UNUSED, const Eina_Value v) +_eo_future2_ok(Eo *eo EINA_UNUSED, void *data EINA_UNUSED, const Eina_Value v) { //Should not happen fail_if(EINA_TRUE); @@ -834,7 +834,7 @@ _eo_future2_ok(Eo *eo EINA_UNUSED, const Eina_Value v) } static Eina_Value -_eo_future2_err(Eo *eo EINA_UNUSED, Eina_Error err) +_eo_future2_err(Eo *eo EINA_UNUSED, void *data EINA_UNUSED, Eina_Error err) { Eina_Value v; @@ -845,7 +845,7 @@ _eo_future2_err(Eo *eo EINA_UNUSED, Eina_Error err) } static void -_eo_future_free(Eo *eo, const Eina_Future *dead EINA_UNUSED) +_eo_future_free(Eo *eo, void *data EINA_UNUSED, const Eina_Future *dead EINA_UNUSED) { int *free_called = efl_key_data_get(eo, "free_called"); (*free_called)++; @@ -890,7 +890,7 @@ EFL_START_TEST(efl_test_promise_eo) EFL_END_TEST static Eina_Value -_eo_future_link_success(Eo *eo EINA_UNUSED, const Eina_Value v) +_eo_future_link_success(Eo *eo EINA_UNUSED, void *data EINA_UNUSED, const Eina_Value v) { //This should never happen fail_if(EINA_TRUE); @@ -898,7 +898,7 @@ _eo_future_link_success(Eo *eo EINA_UNUSED, const Eina_Value v) } static Eina_Value -_eo_future_link_err(Eo *eo, Eina_Error err) +_eo_future_link_err(Eo *eo, void *data EINA_UNUSED, Eina_Error err) { int *err_called = efl_key_data_get(eo, "err_called"); Eina_Value v;