diff options
author | Cedric BAIL <cedric@osg.samsung.com> | 2017-06-29 16:48:03 -0700 |
---|---|---|
committer | Cedric BAIL <cedric@osg.samsung.com> | 2017-06-29 16:48:03 -0700 |
commit | dd6c93cb2358b35cbc8ba90189ddae9d9f4d702f (patch) | |
tree | 94bcb2b35172500e056afe3a014c7b04e2b783c8 | |
parent | c1331adbdc65788c29567009ffa60353aa1959ab (diff) | |
download | efl-devs/cedric/future.tar.gz |
eo: work in progress with new future.devs/cedric/future
-rw-r--r-- | src/lib/eo/efl_future.c | 324 | ||||
-rw-r--r-- | src/lib/eo/efl_future.h | 100 |
2 files changed, 333 insertions, 91 deletions
diff --git a/src/lib/eo/efl_future.c b/src/lib/eo/efl_future.c index aaa1d88a51..71e402af4e 100644 --- a/src/lib/eo/efl_future.c +++ b/src/lib/eo/efl_future.c @@ -5,52 +5,316 @@ #include <Eina.h> #include "Eo.h" -// Efl.Future implementation is an opaque type in Ecore. -EOAPI const Efl_Event_Description _EFL_FUTURE_EVENT_FAILURE = - EFL_EVENT_DESCRIPTION("future,failure"); -EOAPI const Efl_Event_Description _EFL_FUTURE_EVENT_SUCCESS = - EFL_EVENT_DESCRIPTION("future,success"); -EOAPI const Efl_Event_Description _EFL_FUTURE_EVENT_PROGRESS = - EFL_EVENT_DESCRIPTION("future,progress"); - -EOAPI EFL_FUNC_BODYV(efl_future_then, Efl_Future *, 0, EFL_FUNC_CALL(success, failure, progress, data), Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data); -EOAPI EFL_VOID_FUNC_BODY(efl_future_cancel); - -static Eina_Bool -_efl_future_class_initializer(Efl_Class *klass) -{ - EFL_OPS_DEFINE(ops, - EFL_OBJECT_OP_FUNC(efl_future_then, NULL), - EFL_OBJECT_OP_FUNC(efl_future_cancel, NULL)); - - return efl_class_functions_set(klass, &ops, NULL); -} - -static const Efl_Class_Description _efl_future_class_desc = { - EO_VERSION, - "Efl.Future", - EFL_CLASS_TYPE_REGULAR_NO_INSTANT, - 0, - _efl_future_class_initializer, - NULL, - NULL +typedef struct _Efl_Future_Chain Efl_Future_Chain; +typedef struct _Efl_Future_Steal Efl_Future_Steal; + +struct _Efl_Future_Chain +{ + EINA_INLIST; + + Efl_Promise *next; + + Efl_Future_Success_Cb success; + Efl_Future_Failure_Cb failure; + Efl_Future_Progress_Cb progress; + + void *data; }; -EFL_DEFINE_CLASS(efl_future_class_get, &_efl_future_class_desc, EFL_OBJECT_CLASS, NULL); +struct _Efl_Future_Steal +{ + Efl_Promise *next; + + Efl_Future_Success_Steal_Cb success; + Efl_Future_Failure_Cb failure; + Efl_Future_Progress_Cb progress; + + void *data; +}; + +struct _Efl_Future +{ + Eina_Inlist *chains_start; + + Efl_Future_Steal *stealer; + + Efl_Future ***wrefs; + + Eo *parent; + + struct { + Efl_Future_Value *value; + Eina_Error error; + } hold; + + Eina_Bool loop_iterated : 1; + Eina_Bool data : 1; + Eina_Bool error : 1; +}; + +static Eina_Mempool *mpf = NULL; +static Eina_Mempool *mpfc = NULL; + +static Efl_Class *promise = NULL; static const char EINA_ERROR_FUTURE_CANCEL_STR[] = "Future cancelled"; EAPI Eina_Error EINA_ERROR_FUTURE_CANCEL; +static inline Eina_Bool +efl_future_check(Efl_Future *f) +{ +#ifdef DEBUG + return eina_mempool_from(mpf, f); +#endif + return EINA_TRUE; +} + +EAPI Efl_Future * +efl_future_add(Eo *parent) +{ + Efl_Future *f; + + f = eina_mempool_malloc(mpf, sizeof (Efl_Future)); + if (!f) return NULL; + + f->chains_start = NULL; + f->stealer = NULL; + f->wrefs = NULL; + f->loop_iterated = EINA_FALSE; + f->data = EINA_FALSE; + f->error = EINA_FALSE; + + // FIXME: Track lifetime of parent + f->parent = parent; + + return f; +} + +EAPI Efl_Future * +efl_future_then(Efl_Future *f, + Efl_Future_Success_Cb success, + Efl_Future_Failure_Cb failure, + Efl_Future_Progress_Cb progress, + void *data) +{ + Efl_Future_Chain *fc; + + if (!promise) + { + ERR("Initialization of Future hasn't been done properly"); + return NULL; + } + + if (!efl_future_check(f)) return NULL; + + fc = eina_mempool_malloc(mpfc, sizeof (Efl_Future_Chain)); + if (!fc) return NULL; + + fc->next = efl_add(promise, f->parent); + if (!fc->next) goto on_error; + + fc->success = success; + fc->failure = failure; + fc->progress = progress; + fc->data = data; + + f->chains_start = eina_inlist_append(f->chains_start, EINA_INLIST_GET(fc)); + + return efl_promise_future_get(fc->next); +} + +EAPI Efl_Future * +efl_future_steal_then(Efl_Future *f, + Efl_Future_Success_Steal_Cb success, + Efl_Future_Failure_Cb failure, + Efl_Future_Progress_Cb progress, + void *data) +{ + Efl_Future_Steal *stealer; + + if (!efl_future_check(f)) return NULL; + if (f->stealer) + { + ERR("There is already a stealing callback pair registered on future %p. There can only be one.", f); + return NULL; + } + // FIXME: Check promise parent to see if there is already a stealer there + + stealer = eina_mempool_malloc(mpfs, sizeof (Efl_Future_Steal)); + if (!stealer) return NULL; + + stealer->next = efl_add(promise, f->parent); + if (!stealer->next) goto on_error; + + stealer->success = success; + stealer->failure = failure; + stealer->progress = progress; + stealer->data = data; + + f->stealer = stealer; + + return efl_promise_future_get(stealer->next); +} + +EAPI void +efl_future_cancel(Efl_Future *f) +{ + efl_future_error_set(f, EINA_ERROR_FUTURE_CANCEL); +} + +EAPI void +efl_future_use(Efl_Future **storage, Efl_Future *future) +{ + unsigned int count = 0; + Efl_Future ***tmp; + + if (!efl_future_check(f)) return NULL; + + if (future->wrefs) + { + for (; future->wrefs[count]; count++) + ; + } + count++; /* New wref */ + + tmp = realloc(future->wrefs, sizeof (Efl_Future **) * (count + 1)); + if (tmp) + { + future->wrefs = tmp; + future->wrefs[count - 1] = storage; + future->wrefs[count] = NULL; + *storage = future; + } +} + +EAPI void +efl_future_propagate(Efl_Future *f) +{ + Efl_Future_Chain *fc; + + EINA_INLIST_FREE(f->chains_start, fc) + { + if (f->error) fc->failure(fc->data, fc->next, f->hold.error); + else fc->success(fc->data, fc->next, f->hold.value->data); + eina_mempool_free(mpfc, fc); + } + + if (f->stealer) + { + if (EINA_REFCOUNT_GET(f->hold.value->data) > 1) + { + ERR("Future %p should have been called to propagate value the last, but hasn't.", f); + goto on_error; + } + + if (f->error) f->stealer->failure(f->stealer->data, f->stealer->next, f->hold.error); + else f->stealer->success(f->stealer->data, f->stealer->next, f->hold.value->data); + + eina_mempool_free(mpfs, f->stealer); + f->stealer = NULL; + + // No need to free the value has it has been stolen. + } + else + { + on_error: + EINA_REFCOUNT_UNREF(f->hold.value) + { + } + } +} + +EAPI Efl_Future_Value * +efl_future_value_new(void *data, Eina_Free_Cb cb) +{ + +} + +EAPI void +efl_future_value_free(Efl_Future_Value *value) +{ + if (value->cb) + value->cb(value->data); + eina_mempool_free(mpv, value); + value = NULL; +} + +EAPI Eina_Bool +efl_future_loop_iteration(Efl_Future *f) +{ + f->loop_iterated = EINA_TRUE; + + if (f->data || f->error) return EINA_TRUE; + return EINA_FALSE; +} + +EAPI Eina_Bool +efl_future_data_set(Efl_Future *f, Efl_Future_Value *value) +{ + if (f->data || f->error) + { + ERR("Failed to set data on %f which is already finalized.", f); + return EINA_FALSE; + } + + f->data = EINA_TRUE; + f->hold.value = value; + EINA_REFCOUNT_REF(f->hold.value); + + if (!f->loop_iterated) return EINA_FALSE; + return EINA_TRUE; +} + +EAPI Eina_Bool +efl_future_error_set(Efl_Future *f, Eina_Error error) +{ + f->error = EINA_TRUE; + f->hold.error = error; + + if (!f->loop_iterated) return EINA_FALSE; + return EINA_TRUE; +} + +EAPI void +efl_future_prepare(Efl_Future *f) +{ + unsigned int i; + + if (f->wrefs == NULL) return ; + + // Clears up all reference + for (i = 0; f->wrefs[i]; i++) + *f->wrefs[i] = NULL; +} + +EAPI void +efl_future_promise_class_set(Efl_Class *p) +{ + promise = p; +} + Eina_Bool efl_future_init(void) { EINA_ERROR_FUTURE_CANCEL = eina_error_msg_static_register(EINA_ERROR_FUTURE_CANCEL_STR); + mpf = eina_mempool_add("chained_mempool", "test", NULL, sizeof (Efl_Future), 64); + mpfc = eina_mempool_add("chained_mempool", "test", NULL, sizeof (Efl_Future_Chain), 64); + mpfs = eina_mempool_add("chained_mempool", "test", NULL, sizeof (Efl_Future_Steal), 64); return EINA_TRUE; } Eina_Bool efl_future_shutdown(void) { + eina_mempool_del(mpf); + mpf = NULL; + + eina_mempool_del(mpfc); + mpfc = NULL; + + eina_mempool_del(mpfs); + mpfs = NULL; + return EINA_TRUE; } diff --git a/src/lib/eo/efl_future.h b/src/lib/eo/efl_future.h index 34c5d2dcaf..ce55228dbc 100644 --- a/src/lib/eo/efl_future.h +++ b/src/lib/eo/efl_future.h @@ -13,75 +13,51 @@ typedef Eo Efl_Promise; #define _EFL_PROMISE_EO_CLASS_TYPE -#define EFL_FUTURE_CLASS efl_future_class_get() -EWAPI const Efl_Class *efl_future_class_get(void); /**< @since 1.19 */ +typedef struct _Efl_Future Efl_Future; -/** - * @var EINA_ERROR_FUTURE_CANCEL - * @brief The error identifier corresponding to a future being canceled. - */ EAPI extern Eina_Error EINA_ERROR_FUTURE_CANCEL; /**< @since 1.19 */ -/** Parameter passed in event callbacks in case of future failure to proceed. - * - * @ingroup Efl_Future - */ -typedef struct _Efl_Future_Event_Failure Efl_Future_Event_Failure; -struct _Efl_Future_Event_Failure -{ - Efl_Promise *next; /** The promise to the next future. Allowing to send a processed value down the chain. */ - Eina_Error error; /** The error generated trying to process the request. */ -}; +typedef void (*Efl_Future_Success_Cb)(void *data, Efl_Promise *next, const void *value); +typedef void (*Efl_Future_Success_Steal_Cb)(void *data, Efl_Promise *next, void *value); +typedef void (*Efl_Future_Failure_Cb)(void *data, Efl_Promise *next, Eina_Error error); +typedef void (*Efl_Future_Progress_Cb)(void *data, Efl_Promise *next, const void *progress); -/** Parameter passed in event callbacks in case of future succeeding to proceed. +/** + * @brief Add sets of callbacks to handle the progress and result of a future. * - * @ingroup Efl_Future - */ -typedef struct _Efl_Future_Event_Success Efl_Future_Event_Success; -struct _Efl_Future_Event_Success -{ - Efl_Promise *next; /** The promise to the next future. Allowing to send a processed value down the chain. */ - void *value; /** The value return by the processed request. The type is defined by the function executing the request. */ -}; - -/** Parameter passed in event callbacks while a future is progressing a request. + * callbacks are called depending on the outcome of the promise related to the future. * - * @ingroup Efl_Future - */ -typedef struct _Efl_Future_Event_Progress Efl_Future_Event_Progress; -struct _Efl_Future_Event_Progress -{ - Efl_Promise *next; /** The promise to the next future. Allowing to send a processed progress down the chain. */ - const void *progress; /** The progress status updated by the processed request. The type is defined by the function executing the request. */ -}; - -EOAPI extern const Efl_Event_Description _EFL_FUTURE_EVENT_FAILURE; -EOAPI extern const Efl_Event_Description _EFL_FUTURE_EVENT_SUCCESS; -EOAPI extern const Efl_Event_Description _EFL_FUTURE_EVENT_PROGRESS; - -/** A future failed + * @param[in] f the future to track + * @param[in] success the callback to call in case of a succesful computation from the promise + * @param[in] failure the callback to call in case of a failure to deliver from the promise + * @param[in] progress the callback to call during the progression of the the promise, this is optional + * @param[in] data additional data to pass to the callback * - * @ingroup Efl_Future - */ -#define EFL_FUTURE_EVENT_FAILURE (&(_EFL_FUTURE_EVENT_FAILURE)) - -/** A future succeeded + * @return Return a new future when the callback has been successfully added pointing to the next request + * being processed during the future success, failure or progress callback (You can reference count the next + * promise to defer the result and make it asynchronous too). This future can be ignored. * - * @ingroup Efl_Future - */ -#define EFL_FUTURE_EVENT_SUCCESS (&(_EFL_FUTURE_EVENT_SUCCESS)) - -/** A future progressed + * It is assumed that you are going to steal the value of this future and will + * deal by yourself of its life. There can only be one steal and it will be + * called last, once all the other then have been processed (It will technically + * wait for the next loop iteration to be propagated). + * + * @note except if you do reference count the Efl.Future object, you can only call once this function. * * @ingroup Efl_Future */ -#define EFL_FUTURE_EVENT_PROGRESS (&(_EFL_FUTURE_EVENT_PROGRESS)) +EAPI Efl_Future *efl_future_steal_then(Efl_Future *f, + Efl_Future_Success_Steal_Cb success, + Efl_Future_Failure_Cb failure, + Efl_Future_Progress_Cb progress, + void *data); /** - * @brief Add sets of callbacks to handle the progress and the result of a future. + * @brief Add sets of callbacks to handle the progress and result of a future. * * callbacks are called depending on the outcome of the promise related to the future. * + * @param[in] f the future to track * @param[in] success the callback to call in case of a succesful computation from the promise * @param[in] failure the callback to call in case of a failure to deliver from the promise * @param[in] progress the callback to call during the progression of the the promise, this is optional @@ -93,9 +69,15 @@ EOAPI extern const Efl_Event_Description _EFL_FUTURE_EVENT_PROGRESS; * * @note except if you do reference count the Efl.Future object, you can only call once this function. * + * @since 1.20 + * * @ingroup Efl_Future */ -EOAPI Efl_Future *efl_future_then(Eo *obj, Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data); +EAPI Efl_Future *efl_future_then(Efl_Future *f, + Efl_Future_Success_Cb success, + Efl_Future_Failure_Cb failure, + Efl_Future_Progress_Cb progress, + void *data); /** * @brief Cancel the need for that specific future. @@ -107,9 +89,9 @@ EOAPI Efl_Future *efl_future_then(Eo *obj, Efl_Event_Cb success, Efl_Event_Cb fa * * @ingroup Efl_Future * - * @since 1.19 + * @since 1.20 */ -EOAPI void efl_future_cancel(Eo *obj); +EAPI void efl_future_cancel(Efl_Future *f); /** * @brief To be used in conjunction with when you plan to use efl_future_cancel @@ -125,11 +107,7 @@ EOAPI void efl_future_cancel(Eo *obj); * * @ingroup Efl_Future */ -static inline void -efl_future_use(Efl_Future **storage, Eo *future) -{ - efl_wref_add(future, storage); -} +EAPI void efl_future_use(Efl_Future **storage, Efl_Future *future); /** * @} |