#ifndef _EINA_PROMISE_H_ #define _EINA_PROMISE_H_ #ifdef __cplusplus extern "C" { #endif #include "eina_safety_checks.h" #include "eina_types.h" #include "eina_value.h" /** * @ingroup Eina_Promise * * @{ */ typedef struct _Eina_Future_Desc Eina_Future_Desc; typedef struct _Eina_Promise Eina_Promise; typedef struct _Eina_Future Eina_Future; typedef struct _Eina_Future_Cb_Easy_Desc Eina_Future_Cb_Easy_Desc; typedef struct _Eina_Future_Cb_Console_Desc Eina_Future_Cb_Console_Desc; typedef struct _Eina_Future_Scheduler Eina_Future_Scheduler; typedef struct _Eina_Future_Schedule_Entry Eina_Future_Schedule_Entry; typedef struct _Eina_Future_Race_Result Eina_Future_Race_Result; typedef struct _Eina_Future_Cb_Log_Desc Eina_Future_Cb_Log_Desc; /** * @defgroup Eina_Future_Callbacks Efl Future Callbacks * @ingroup eina_future * @typedef Eina_Future_Cb Eina_Future_Cb * * A callback used to inform that a future was resolved. * Usually this callback is called from a clean context, that is, from the * main loop or some platform defined safe context. However there are * 2 exceptions: * * @li eina_future_cancel() was used, it's called immediately in the * context that called cancel using `ECANCELED` as error. * * @li eina_future_then(), eina_future_then_from_desc(), eina_future_chain(), eina_future_chain_array() * or similar failed due to invalid pointer or memory allocation. Then the callback is called from the * failed context using `EINVAL` or `ENOMEM` as errors and @p dead_future will be @c NULL. * * @param[in] data The data provided by the user * * @param[in] value An Eina_Value which contains the operation result. Before using * the @p value, its type must be checked in order to avoid errors. This is needed because * if an operation fails the Eina_Value type will be EINA_VALUE_TYPE_ERROR * which is a different type than the expected operation result. * * @param[in] dead_future A pointer to the future that was completed. * * @return An Eina_Value to pass to the next Eina_Future in the chain (if any). * If there is no need to convert the received value, it's @b recommended * to passthrough @p value argument. If you need to convert to a different type * or generate a new value, use @c eina_value_setup() on @b another Eina_Value * and return it. By returning a promise Eina_Value (eina_promise_as_value()) the * whole chain will wait until the promise is resolved in * order to continue its execution. * Note that the value contents must survive this function scope, * that is, do @b not use stack allocated blobs, arrays, structures or types that * keep references to memory you give. Values will be automatically cleaned up * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). * * @note The returned value @b can be an EFL_VALUE_TYPE_PROMISE! (see eina_promise_as_value() and * eina_future_as_value()) In this case the future chain will wait until the promise is resolved. * * @see eina_future_cancel() * @see eina_future_then() * @see eina_future_then_from_desc() * @see eina_future_then_easy() * @see eina_future_chain() * @see eina_future_chain_array() * @see eina_future_as_value() * @see eina_promise_as_value() * @{ */ typedef Eina_Value (*Eina_Future_Cb)(void *data, const Eina_Value value, const Eina_Future *dead_future); /** * @struct _Eina_Future_Scheduler * @ingroup eina_promise * * A struct that represents an scheduled event. * This struct may be used by Eina to cancel * a scheduled future. * * @see eina_promise_new() * * @see #Eina_Future_Scheduler * @see #Eina_Future_Scheduler_Cb */ struct _Eina_Future_Schedule_Entry { /** * The scheduler used to create the entry. * @note This must not be @c NULL. */ Eina_Future_Scheduler *scheduler; }; /** * @typedef Eina_Future_Scheduler_Cb * @ingroup eina_promise * A callback used by the Eina_Future_Scheduler to deliver * the future operation result. * * @param[out] f The delivered future. * @param[in] value The future result * * * @see eina_promise_new() * * @see #Eina_Future_Schedule_Entry * @see #Eina_Future_Scheduler */ typedef void (*Eina_Future_Scheduler_Cb)(Eina_Future *f, Eina_Value value); /** * @struct _Eina_Future_Scheduler * @ingroup eina_promise * This struct is used as a bridge between Eina and the future scheduler. * By using the functions provided by #_Eina_Future_Scheduler Eina can * schedule futures resolutions, rejections and cancellations to a safe context. * * @see eina_promise_new() * @see #Eina_Future_Schedule_Entry * @see #Eina_Future_Scheduler_Cb */ struct _Eina_Future_Scheduler { /** * Called by @p Eina_Future when a delivery must be scheduled to a safe context. * i.e.: after @p eina_promise_resolve() * * @note Must not be @c NULL * * Must call back from a safe context using @p cb(f,value) * @param[in,out] scheduler The scheduler to use. * @param[in] cb The #Eina_Future_Scheduler_Cb to be called and deliver the @p f and @p value. * @param[in] f The future to be delivered to @p cb * @param[in] value The value to be delivered to @p cb * @return A scheduled entry or @c NULL on error */ Eina_Future_Schedule_Entry *(*schedule)(Eina_Future_Scheduler *scheduler, Eina_Future_Scheduler_Cb cb, Eina_Future *f, Eina_Value value); /** * Called by @p Eina_Future when a delivery must be canceled. * i.e.: after @p eina_future_cancel() * * @note Must not be @c NULL. * * @param[in,out] entry The scheduled event to cancel */ void (*recall)(Eina_Future_Schedule_Entry *entry); }; /** * @typedef Eina_Promise_Cancel_Cb Eina_Promise_Cancel_Cb. * @ingroup eina_promise * * A callback used to inform that a promise was canceled. * * A promise may be canceled by the user calling `eina_future_cancel()` * on any Eina_Future that is part of the chain that uses an Eina_Promise, * that will cancel the whole chain and then the promise. * * It should stop all asynchronous operations or at least mark them to be * discarded instead of resolved. Actually it can't be resolved once * canceled since the given pointer @c dead_promise is now invalid. * * @note This callback is @b mandatory for a reason, do not provide an empty * callback as it'll likely result in memory corruption and invalid access. * If impossible to cancel an asynchronous task, then create an * intermediate memory to hold Eina_Promise and make it @c NULL, * in this callback. Then prior to resolution check if the pointer is set. * * @note This callback is @b not called if eina_promise_resolve() or * eina_promise_reject() are used. If any cleanup is needed, then * call yourself. It's only meant as cancellation, not a general * "free callback". * * @param[in] data The data provided by the user. * @param[in] dead_promise The canceled promise. * @see eina_promise_reject() * @see eina_promise_resolve() * @see eina_future_cancel() */ typedef void (*Eina_Promise_Cancel_Cb) (void *data, const Eina_Promise *dead_promise); /** * @typedef Eina_Future_Success_Cb Eina_Future_Success_Cb. * @ingroup eina_future * * A callback used to inform that the future completed with success. * * Unlike #Eina_Future_Cb this callback only called if the future operation was successful, this is, * no errors occurred (@p value type differs from EINA_VALUE_TYPE_ERROR) * and the @p value matches #_Eina_Future_Cb_Easy_Desc::success_type (if given). * In case #_Eina_Future_Cb_Easy_Desc::success_type was not supplied (it's @c NULL) the @p value type * must be checked before using it. * * @note This function is always called from a safe context (main loop or some platform defined safe context). * * @param[in] data The data provided by the user. * @param[in] value The operation result * @return An Eina_Value to pass to the next Eina_Future in the chain (if any). * If there is no need to convert the received value, it's @b recommended * to passthrough @p value argument. If you need to convert to a different type * or generate a new value, use @c eina_value_setup() on @b another Eina_Value * and return it. By returning a promise Eina_Value (eina_promise_as_value()) the * whole chain will wait until the promise is resolved in * order to continue its execution. * Note that the value contents must survive this function scope, * that is, do @b not use stack allocated blobs, arrays, structures or types that * keep references to memory you give. Values will be automatically cleaned up * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() */ typedef Eina_Value (*Eina_Future_Success_Cb)(void *data, const Eina_Value value); /** * @typedef Eina_Future_Error_Cb Eina_Future_Error_Cb. * @ingroup eina_future * * A callback used to inform that the future completed with failure. * * Unlike #Eina_Future_Success_Cb this function is only called when an error * occurs during the future process or when #_Eina_Future_Cb_Easy_Desc::success_type * differs from the future result. * On future creation errors and future cancellation this function will be called * from the current context with the following errors respectfully: `EINVAL`, `ENOMEM` and `ECANCELED`. * Otherwise this function is called from a safe context. * * If it was possible to recover from an error this function should return an empty value * `return EINA_VALUE_EMPTY;` or any other Eina_Value type that differs from EINA_VALUE_TYPE_ERROR. * In this case the error will not be reported by the other futures in the chain (if any), otherwise * if an Eina_Value type EINA_VALUE_TYPE_ERROR is returned the error will continue to be reported * to the other futures in the chain. * * @param[in] data The data provided by the user. * @param[in] error The operation error * @return An Eina_Value to pass to the next Eina_Future in the chain (if any). * If you need to convert to a different type or generate a new value, * use @c eina_value_setup() on @b another Eina_Value * and return it. By returning a promise Eina_Value (eina_promise_as_value()) the * whole chain will wait until the promise is resolved in * order to continue its execution. * Note that the value contents must survive this function scope, * that is, do @b not use stack allocated blobs, arrays, structures or types that * keep references to memory you give. Values will be automatically cleaned up * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() */ typedef Eina_Value (*Eina_Future_Error_Cb)(void *data, const Eina_Error error); /** * @typedef Eina_Future_Free_Cb Eina_Future_Free_Cb. * @ingroup eina_future * * A callback used to inform that the future was freed and the user should also @c free the @p data. * This callback may be called from an unsafe context if the future was canceled or an error * occurred. * * @note This callback is always called, even if #Eina_Future_Error_Cb and/or #Eina_Future_Success_Cb * were not provided, which can also be used to monitor when a future ends. * * @param[in] data The data provided by the user. * @param[in] dead_future The future that was freed. * * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() */ typedef void (*Eina_Future_Free_Cb)(void *data, const Eina_Future *dead_future); /** * @struct _Eina_Future_Cb_Easy_Desc * @ingroup eina_future * * A struct with callbacks to be used by eina_future_cb_easy_from_desc() and eina_future_cb_easy() * * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() */ struct _Eina_Future_Cb_Easy_Desc { /** * Called on success (value.type is not @c EINA_VALUE_TYPE_ERROR). * * if @c success_type is not NULL, then the value is guaranteed to be of that type, * if it's not, then it will trigger @c error with @c EINVAL. * * After this function returns, @c free callback is called if provided. */ Eina_Future_Success_Cb success; /** * Called on error (value.type is @c EINA_VALUE_TYPE_ERROR). * * This function can return another error, propagating or converting it. However it * may also return a non-error, in this case the next future in chain will receive a regular * value, which may call its @c success. * * If this function is not provided, then it will passthrough the error to the next error handler. * * It may be called with @c EINVAL if @c success_type is provided and doesn't * match the received type. * * It may be called with @c ECANCELED if future was canceled. * * It may be called with @c ENOMEM if memory allocation failed during callback creation. * * After this function returns, @c free callback is called if provided. */ Eina_Future_Error_Cb error; /** * Called on @b all situations to notify future destruction. * * This is called after @c success or @c error, as well as it's called if none of them are * provided. Thus can be used as a "weak ref" mechanism. */ Eina_Future_Free_Cb free; /** * If provided, then @c success will only be called if the value type matches the given pointer. * * If provided and doesn't match, then @c error will be called with @c EINVAL. If no @c error, * 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; }; /** * @struct _Eina_Future_Cb_Console_Desc * @ingroup eina_future * * A struct used to define the prefix and suffix to be printed * along side the a future value. This struct is used by * eina_future_cb_console_from_desc() * * @see eina_future_cb_console_from_desc() */ struct _Eina_Future_Cb_Console_Desc { /** * The prefix to be printed. If @c NULL an empty string ("") is used. */ const char *prefix; /** * The suffix the be printed. If @c NULL '\n' is used. */ const char *suffix; }; /** * @struct _Eina_Future_Cb_Log_Desc * @ingroup eina_future * * This struct is used by eina_future_cb_log_from_desc() and * its contents will be routed to eina_log_print() along side * the future value. * * @see eina_future_cb_log_from_desc() */ struct _Eina_Future_Cb_Log_Desc { /** * The prefix to be printed. If @c NULL an empty string ("") is used. */ const char *prefix; /** * The suffix the be printed. If @c NULL '\n' is used. */ const char *suffix; /** * The file name to be passed to eina_log_print(). if @c NULL "Unknown file" is used. */ const char *file; /** * The file name to be passed to eina_log_print(). if @c NULL "Unknown function" is used. */ const char *func; /** * The Eina_Log_Level to use. */ Eina_Log_Level level; /** * The log domain to use. */ int domain; /** * The line number to be passed to eina_log_print(). */ int line; }; /** * @struct _Eina_Future_Desc * @ingroup eina_future * A struct used to define a callback and data for a future. * * This struct contains a future completion callback and a data to the future * completion callback which is used by eina_future_then(), eina_future_chain() * and friends to inform the user about the future result. The #_Eina_Future_Desc::data * variable should be freed when #_Eina_Future_Desc::cb is called, otherwise it will leak. * * @note If eina_future_then(), eina_future_chain() and friends fails to return a valid future * (in other words: @c NULL is returned) the #_Eina_Future_Desc::cb will be called * report an error like `EINVAL` or `ENOMEM` so #_Eina_Future_Desc::data can be freed. * * @see eina_future_then() * @see eina_future_chain_array() * @see eina_future_cb_convert_to() * @see eina_future_cb_console_from_desc() * @see eina_future_cb_ignore_error() * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_log_from_desc() */ struct _Eina_Future_Desc { /** * Called when the future is resolved or rejected. * * Once a future is resolved or rejected this function is called passing the future result * to inform the user that the future operation has ended. Normally this * function is called from a safe context (main loop or some platform defined safe context), * however in case of a future cancellation (eina_future_cancel()) or if eina_future_then(), * eina_future_chain() and friends fails to create a new future, * this function is called from the current context. * * Use this function to free @p data if necessary. * @see eina_future_chain() * @see eina_future_then() * @see eina_future_cancel() */ Eina_Future_Cb cb; /** * Context data to @p cb. The @p data should be freed inside @p cb, otherwise * its memory will leak! */ const void *data; /** * The storage will be used by Eina to store a pointer to the * created future. It can be @c NULL. */ Eina_Future **storage; }; /** * @} */ /** * @defgroup Eina_Promise Eina_Promise * Creates a new promise. * * This function creates a new promise which can be used to create a future * using eina_future_new(). Every time a promise is created a #Eina_Promise_Cancel_Cb * must be provided which is used to free resources that were created. * * A promise may be canceled directly by calling * @c eina_future_cancel(eina_future_new(eina_promise_new(...))) * that is, canceling any future that is chained to receive the results. * * However promises can be canceled indirectly by other entities. * These other entities will call `eina_future_cancel()` themselves, * however you may not be aware of that. Some common sources * of indirect cancellations: * * @li A subsystem was shutdown, canceling all pending futures (i.e.: ecore_shutdown()) * * @li An EO object was linked to the promise or future, then if the object dies (last reference * is gone), then the pending promises and futures will be canceled. * * @li Some other entity (library provider or library user) chained and canceled his future, * which will result in your future being canceled. * * Since a promise may be canceled indirectly (by code sections that goes beyond your scope) * you should always provide a cancel callback, even if you think you'll not need it. * * Here's a typical example: * * @code * #include * * static void * _promise_cancel(void *data, Eina_Promise *p EINA_UNUSED) * { * Ctx *ctx = data; * // In case the promise is canceled we must stop the timer! * ecore_timer_del(ctx->timer); * free(ctx); * } * * static Eina_Bool * _promise_resolve(void *data) * { * Ctx *ctx = data; * Eina_Value v; * eina_value_setup(&v, EINA_VALUE_TYPE_STRING); * eina_value_set(&v, "Promise resolved"); * eina_promise_resolve(ctx->p, v); * free(ctx); * return EINA_FALSE; * } * * Eina_Promise * * promise_create(Eina_Future_Scheduler *scheduler) * { * Ctx *ctx = malloc(sizeof(Ctx)); * // A timer is scheduled in order to resolve the promise * ctx->timer = ecore_timer_add(122, _promise_resolve, ctx); * // The _promise_cancel() will be used to clean ctx if the promise is canceled. * ctx->p = eina_promise_new(scheduler, _promise_cancel, ctx); * return ctx->p; * } * @endcode * * If you already have a value and want to create a future that will * resolve to it directly use the eina_future_resolved(), it has the * same effect as creating a promise and immediately resolving it. * * @param[in,out] scheduler The scheduler. * @param[in] cancel_cb A callback used to inform that the promise was canceled. Use * this callback to @c free @p data. @p cancel_cb must not be @c NULL ! * @param[in] data Data to @p cancel_cb. * @return A promise or @c NULL on error. * @see eina_future_cancel() * @see eina_future_new() * @see eina_promise_continue_new() * @see eina_promise_resolve() * @see eina_promise_reject() * @see eina_promise_as_value() * @see #Eina_Future_Scheduler * @see #Eina_Future_Scheduler_Entry * @see #Eina_Future_Scheduler_Cb * @{ */ EAPI Eina_Promise *eina_promise_new(Eina_Future_Scheduler *scheduler, Eina_Promise_Cancel_Cb cancel_cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; /** * Creates a new promise from a dead_future. * * This function creates a new promise from a future currently being resolved which can be * used to create a #Eina_Value with eina_promise_as_value(). Every time a promise is * created a #Eina_Promise_Cancel_Cb must be provided which is used to free resources * that were created. * * A promise may be canceled directly by calling * @c eina_future_cancel(eina_future_new(eina_promise_new(...))) * that is, canceling any future that is chained to receive the results. * * However promises can be canceled indirectly by other entities. * These other entities will call `eina_future_cancel()` themselves, * however you may not be aware of that. Some common sources * of indirect cancellations: * * @li A subsystem was shutdown, canceling all pending futures (i.e.: ecore_shutdown()) * * @li An EO object was linked to the promise or future, then if the object dies (last reference * is gone), then the pending promises and futures will be canceled. * * @li Some other entity (library provider or library user) chained and canceled his future, * which will result in your future being canceled. * * Since a promise may be canceled indirectly (by code sections that goes beyond your scope) * you should always provide a cancel callback, even if you think you'll not need it. * * Here's a typical example: * * @code * * Eina_Value * _future_resolve(void *data, const Eina_Value v, const Eina_Future *dead_future) * { * Eina_Promise *p; * p = eina_promise_continue_new(dead_future, _promise_cancel, NULL); * return eina_promise_as_value(p); * } * @endcode * * If you already have a value and want to create a future that will * resolve to it directly use the eina_future_resolved(), it has the * same effect as creating a promise and immediately resolving it. * * @note This function is to be used solely inside of a future resolve callback with * the Eina_Value being returned from it. * * @param[in] dead_future The future being resolved to get a scheduler from. * @param[in] cancel_cb A callback used to inform that the promise was canceled. Use * this callback to @c free @p data. @p cancel_cb must not be @c NULL ! * @param[in] data Data to @p cancel_cb. * @return A promise or @c NULL on error. * @see eina_future_cancel() * @see eina_future_new() * @see eina_promise_new() * @see eina_promise_resolve() * @see eina_promise_reject() * @see eina_promise_as_value() * @see #Eina_Future_Scheduler * @see #Eina_Future_Scheduler_Entry * @see #Eina_Future_Scheduler_Cb */ EAPI Eina_Promise *eina_promise_continue_new(const Eina_Future *dead_future, Eina_Promise_Cancel_Cb cancel_cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; /** * Resolves a promise. * * This function schedules a resolve event in a * safe context (main loop or some platform defined safe context), * whenever possible the future callbacks will be dispatched. * * @param[in,out] p A promise to resolve. * @param[in] value The value to be delivered. * * Note that the @p value contents must survive this function scope, * that is, do @b not use stack allocated blobs, arrays, structures or types that * keep references to memory you give. Values will be automatically cleaned up * using @c eina_value_flush() once they are unused (no more future or futures * returned a new value). * * @see eina_promise_new() * @see eina_promise_reject() * @see eina_promise_as_value() */ EAPI void eina_promise_resolve(Eina_Promise *p, Eina_Value value) EINA_ARG_NONNULL(1); /** * Rejects a promise. * * This function schedules a reject event in a * safe context (main loop or some platform defined safe context), * whenever possible the future callbacks will be dispatched. * * @param[in,out] p A promise to reject. * @param[in] err An Eina_Error value * @note Internally this function will create an Eina_Value with type #EINA_VALUE_TYPE_ERROR. * * @see eina_promise_new() * @see eina_promise_resolve() * @see eina_promise_as_value() */ EAPI void eina_promise_reject(Eina_Promise *p, Eina_Error err) EINA_ARG_NONNULL(1); /** * @} */ /** * @defgroup Eina_Future Eina_Future * Cancels a future. * * This function will cancel the whole future chain immediately (it will not be schedule to the next mainloop pass) * and it will also cancel the promise linked against it. The Eina_Future_Cb will be called * with an Eina_Value typed as #EINA_VALUE_TYPE_ERROR, which its value will be * ECANCELED * @param[in,out] f The future to cancel. * @{ */ EAPI void eina_future_cancel(Eina_Future *f) EINA_ARG_NONNULL(1); /** * Flushes an #Eina_Future_Desc * * This function is mainly used by bindings to flush a #Eina_Future_Desc. * It will call the #Eina_Future_Cb with `ENOMEM` and zero the @p desc contents. * * @param[in,out] desc The #Eina_Future_Desc to flush, if @c NULL this is a noop. */ EAPI void eina_future_desc_flush(Eina_Future_Desc *desc); /** * Flushes an #Eina_Future_Cb_Easy_Desc * * This function is mainly used by bindings to flush a #Eina_Future_Cb_Easy_Desc. * It will call the #Eina_Future_Error_Cb with `ENOMEM`, the #Eina_Future_Free_Cb and * zero the @p desc contents. * * @param[in,out] desc The #Eina_Future_Cb_Easy_Desc to flush, if @c NULL this is a noop. */ EAPI void eina_future_cb_easy_desc_flush(Eina_Future_Cb_Easy_Desc *desc); /** * Creates a new Eina_Value from a promise. * * This function creates a new Eina_Value that will store a promise * in it. This function is useful for dealing with promises inside * a #Eina_Future_Cb. By returning a Promise Eina_Value the * whole chain will wait until the promise is resolved in * order to continue its execution. Example: * * @code * static Eina_Value * _file_data_ready(const *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED) * { * const char *file_data; * Eina_Promise *p; * // It was not possible to fetch the file size. * if (v.type == EINA_VALUE_TYPE_ERROR) * { * Eina_Error err; * eina_value_get(&v, &err); * fprintf(stderr, "Could not get the file data. Reason: %s\n", eina_error_msg_get(err)); * ecore_main_loop_quit(); * return v; * } * else if (v.type != EINA_VALUE_TYPE_STRING) * { * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_STRING->name, v.type->name); * ecore_main_loop_quit(); * return v; * } * * eina_value_get(&v, &file_data); * // count_words will count the words in the background and resolve the promise once it is over... * p = count_words(file_data); * return eina_promise_as_value(p); * } * * static Eina_Value * _word_count_ready(const *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED) * { * // The _word_count_ready will only be called once count_words() resolves/rejects the promise returned by _file_data_ready() * int count; * if (v.type == EINA_VALUE_TYPE_ERROR) * { * Eina_Error err; * eina_value_get(&v, &err); * fprintf(stderr, "Could not count the file words. Reason: %s\n", eina_error_msg_get(err)); * ecore_main_loop_quit(); * return v; * } * else if (v.type != EINA_VALUE_TYPE_INT) * { * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_INT->name, v.type->name); * ecore_main_loop_quit(); * return v; * } * eina_value_get(&v, &count); * printf("File word count %d\n", count); * return v; * } * * void * file_operation(void) * { * Eina_Future *f = get_file_data("/MyFile.txt"); * eina_future_chain(f, {.cb = _file_data_ready, .data = NULL}, * {.cb = _word_count_ready, .data = NULL}); * } * @endcode * * @param[in,out] p The promise obect to create the value from. * @return An Eina_Value. On error the value's type will be @c NULL. * * @note If an error happens the promise will be CANCELED. * @see eina_future_as_value() * @see eina_promise_reject() * @see eina_promise_resolve() */ EAPI Eina_Value eina_promise_as_value(Eina_Promise *p) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; /** * Creates an Eina_Value from a future. * * This function is used for the same purposes as eina_promise_as_value(), * but receives an Eina_Future instead. * * @param[in,out] f A future to create a Eina_Value from. * @return An Eina_Value. On error the value's type will be @c NULL. * @note If an error happens the future @p f will be CANCELED * @see eina_promise_as_value() */ EAPI Eina_Value eina_future_as_value(Eina_Future *f)EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; /** * Creates a new future. * * This function creates a new future and can be used to report * that an operation has succeeded or failed using * eina_promise_resolve() or eina_promise_reject(). * * Futures can also be canceled using eina_future_cancel(), which * will cause the whole chain to be canceled alongside with any pending promise. * * @note A promise can only have one future attached to it, calling * eina_future_new() on the same promise twice will * result in an error (@c NULL is returned) and the future * attached to the promise will be canceled! * * @param[in,out] p A promise used to attach a future. May not be @c NULL. * @return The future or @c NULL on error. * @note If an error happens this function will CANCEL the promise. * @see eina_promise_new() * @see eina_promise_reject() * @see eina_promise_resolve() * @see eina_future_cancel() */ EAPI Eina_Future *eina_future_new(Eina_Promise *p) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; /** * Creates a new future that is already resolved to a value. * * This function creates a new future with an already known value, * that will be resolved and dispatched by the given @a scheduler as * usual. * * This is a helper that behaves the same as eina_promise_new() * followed by eina_future_new() and then eina_promise_resolve(). * * Futures can also be canceled using eina_future_cancel(), which will * cause the whole chain to be canceled alongside with any pending * promise. * * @param[in,out] scheduler The scheduler to use. * @param[in] value The value to be delivered. Note that the value * contents must survive this function scope, that is, do @b not use * stack allocated blobs, arrays, structures or types that keep * references to memory you give. Values will be automatically cleaned * up using @c eina_value_flush() once they are unused (no more future * or futures returned a new value). * * @return The future or @c NULL on error. * * @see eina_promise_new() * @see eina_future_new() * @see eina_promise_reject() * @see eina_promise_resolve() * @see eina_future_cancel() */ EAPI Eina_Future *eina_future_resolved(Eina_Future_Scheduler *scheduler, Eina_Value value) EINA_ARG_NONNULL(1); /** * Creates a new future that is already rejected to a specified error. * * This function creates a new future with an already known error, * that will be resolved and dispatched by the given @a scheduler as * usual. * * This is a helper that behaves the same as eina_promise_new() * followed by eina_future_new() and then eina_promise_rejected(). * * Futures can also be canceled using eina_future_cancel(), which will * cause the whole chain to be canceled alongside with any pending * promise. * * @param[in,out] scheduler The scheduler to use. * @param[in] err An Eina_Error value * * @return The future or @c NULL on error. * * @see eina_promise_new() * @see eina_future_new() * @see eina_promise_reject() * @see eina_promise_resolve() * @see eina_future_cancel() */ EAPI Eina_Future *eina_future_rejected(Eina_Future_Scheduler *scheduler, Eina_Error err); /** * Register an Eina_Future_Desc to be used when the future is resolve/rejected. * * With this function a callback and data is attached to the future and then * once it's resolved or rejected the callback will be informed. * * If during the future creation an error happens this function will return @c NULL, * and the #Eina_Future_Desc::cb will be called reporting an error (`EINVAL` or `ENOMEM`), * so the user has a chance to free #Eina_Future_Desc::data. * * In case a future in the chain is canceled, the whole chain will be canceled immediately * and the error `ECANCELED` will be reported. * * Here's a simple usage of this function. * * @code * static Eina_Value * _file_ready(const *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED) * { * Ctx *ctx = data; * // It was not possible to fetch the file size. * if (v.type == EINA_VALUE_TYPE_ERROR) * { * Eina_Error err; * eina_value_get(&v, &err); * fprintf(stderr, "Could not read the file size. Reason: %s\n", eina_error_msg_get(err)); * ecore_main_loop_quit(); * return v; * } * else if (v.type != EINA_VALUE_TYPE_INT) * { * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_INT->name, v.type->name); * ecore_main_loop_quit(); * return v; * } * eina_value_get(&v, &ctx->size); * printf("File size is %d bytes\n", ctx->size); * return v; * } * * void * file_operation(void) * { * Eina_Future *f = get_file_size_async("/MyFile.txt"); * eina_future_then_from_desc(f, (const Eina_Future_Desc){.cb = _size_ready, .data = NULL}); * // There's a helper macro called eina_future_then() which simplifies the usage. * // The code below has the same effect. * // eina_future_then(f, _size_ready, NULL); * } * @endcode * * Although the code presented at `_size_ready()` is very simple, most of it * is just used to check the Eina_Value type. In order * to avoid this type of code the function eina_future_cb_easy_from_desc() * was created. Please, check its documentation for more information. * * This function can also be used to create a future chain, making * it possible to execute the future result in a cascade order. When * using a future chain the Eina_Value returned by a #Eina_Future_Desc::cb * will be delivered to the next #Eina_Future_Desc::cb in the chain. * * Here's an example: * * static Eina_Value * _future_cb1(const *data EINA_UNUSED, const Eina_Value v) * { * Eina_Value new_v; * int i; * * // There's no need to check the Eina_Value type since we're using eina_future_cb_easy() * eina_value_get(&v, &i); * printf("File size as int: %d\n", i); * eina_value_setup(&new_v, EINA_VALUE_TYPE_STRING); * // Convert the file size to string * eina_value_convert(&v, &new_v); * return new_v; * } * * static Eina_Value * _future_cb2(const *data EINA_UNUSED, const Eina_Value v) * { * Eina_Value new_v; * const char *file_size_str; * * // There's no need to check the Eina_Value type since we're using eina_future_cb_easy() * eina_value_get(&v, &file_size_str); * printf("File size as string: %s\n", file_size_str); * eina_value_setup(&new_v, EINA_VALUE_TYPE_DOUBLE); * eina_value_convert(&v, &new_v); * return new_v; * } * * static Eina_Value * _future_cb3(const *data EINA_UNUSED, const Eina_Value v) * { * double d; * * // There's no need to check the Eina_Value type since we're using eina_future_cb_easy() * eina_value_get(&v, &d); * printf("File size as double: %g\n", d); * return v; * } * * static Eina_Value * _future_err(void *data EINA_UNUSED, Eina_Error err) * { * // This function is called if future result type does not match or another error occurred * Eina_Value new_v; * eina_value_setup(&new_v, EINA_VALUE_TYPE_ERROR); * eina_value_set(&new_v, err); * fprintf(stderr, "Error during future process. Reason: %s\n", eina_error_msg_get(err)); * // Pass the error to the next future in the chain.. * return new_v; * } * * @code * void chain(void) * { * Eina_Future *f = get_file_size_async("/MyFile.txt"); * f = eina_future_then_easy(f, .success = _future_cb1, .success_type = EINA_VALUE_TYPE_INT); * // _future_cb2 will be executed after _future_cb1() * f = eina_future_then_easy(f, .success = _future_cb2, .success_type = EINA_VALUE_TYPE_STRING); * // _future_cb3 will be executed after _future_cb2() * f = eina_future_then_easy(f, .success = _future_cb3, .success_type = EINA_VALUE_TYPE_DOUBLE); * // If an error happens _future_err will be called * eina_future_then_easy(f, .error = _future_err); * } * @endcode * * Although it's possible to create a future chain using eina_future_then()/eina_future_then_from_desc() * there's a function that makes this task much easier, please check eina_future_chain_array() for more * information. * @note This example does manual conversion and print, however we offer * eina_future_cb_convert_to() and eina_future_cb_console_from_desc() and to make those common case easier. * * @param[in,out] prev A future to link against * @param[in] desc A description struct containing the callback and data. * @return A new future or @c NULL on error. * @note If an error happens the whole future chain will CANCELED and * desc.cb will be called in order to free desc.data. * @see eina_future_new() * @see eina_future_then() * @see #Eina_Future_Desc * @see eina_future_chain_array() * @see eina_future_chain() * @see eina_future_cb_console_from_desc() * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() * @see eina_future_cb_convert_to() * @see eina_future_cancel() * @see eina_future_then_easy() * @see eina_future_cb_log_from_desc() */ EAPI Eina_Future *eina_future_then_from_desc(Eina_Future *prev, const Eina_Future_Desc desc) EINA_ARG_NONNULL(1); /** * Creates an Eina_Future_Desc that will log the previous future resolved value. * * This function can be used to quickly log the value that an #Eina_Future_Desc::cb * is returning. The returned value will be passed to the next future in the chain without * modifications. * * There are some helper macros like eina_future_cb_log_dbg() which will automatically * fill the following fields: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_DBG will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * * @param[in] desc The description data to be used. * @return An #Eina_Future_Desc * * @see #Eina_Future_Cb_Log_Desc * @see efl_future_then() * @see efl_future_chain() * @see eina_future_cb_log_dbg() * @see eina_future_cb_log_crit() * @see eina_future_cb_log_err() * @see eina_future_cb_log_info() * @see eina_future_cb_log_warn() * @see eina_future_cb_console_from_desc() */ EAPI Eina_Future_Desc eina_future_cb_log_from_desc(const Eina_Future_Cb_Log_Desc desc) EINA_WARN_UNUSED_RESULT; /** * Creates a future chain. * * This behaves exactly like eina_future_then_from_desc(), but makes it easier to create future chains. * * If during the future chain creation an error happens this function will return @c NULL, * and the #Eina_Future_Desc::cb from the @p descs array will be called reporting an error (`EINVAL` or `ENOMEM`), * so the user has a chance to free #Eina_Future_Desc::data. * * Just like eina_future_then_from_desc(), the value returned by a #Eina_Future_Desc::cb callback will * be delivered to the next #Eina_Future_Desc::cb in the chain. * * In case a future in the chain is canceled, the whole chain will be canceled immediately * and the error `ECANCELED` will be reported. * * Here's an example: * * @code * * // callbacks code.... * * Eina_Future* chain(void) * { * Eina_Future *f = get_file_size_async("/MyFile.txt"); * return eina_future_chain(f, eina_future_cb_easy(_future_cb1, _future_err, NULL, EINA_VALUE_TYPE_INT, NULL), * eina_future_cb_easy(_future_cb2, _future_err, NULL, EINA_VALUE_TYPE_STRING, NULL), * eina_future_cb_easy(_future_cb3, _future_err, NULL, EINA_VALUE_TYPE_DOUBLE, NULL), * {.cb = _future_cb4, .data = NULL }); * } * @endcode * * @param[in,out] prev The previous future * @param[in] descs An array of descriptions. The last element of the array must have the #Eina_Future_Desc::cb set to @c NULL * @return A future or @c NULL on error. * @note If an error happens the whole future chain will CANCELED and * desc.cb will be called in order to free desc.data. * @see eina_future_new() * @see eina_future_then() * @see #Eina_Future_Desc * @see eina_future_chain() * @see eina_future_cb_ignore_error() * @see eina_future_cb_console_from_desc() * @see eina_future_cb_log_from_desc() * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() * @see eina_future_then_from_desc() * @see eina_future_then_easy() * @see eina_future_cb_convert_to() */ EAPI Eina_Future *eina_future_chain_array(Eina_Future *prev, const Eina_Future_Desc descs[]) EINA_ARG_NONNULL(1, 2); /** * Wrapper around eina_future_chain_array() and eina_future_cb_easy_from_desc() * * This functions makes it easier to use eina_future_chain_array() with eina_future_cb_easy_from_desc(), * check the macro eina_future_chain_easy() for a syntax sugar. * * * @param[in,out] prev The previous future * @param[in] descs An array of descriptions. The last element of the array must have the #Eina_Future_Desc::cb set to @c NULL * @return A future or @c NULL on error. * @note If an error happens the whole future chain will CANCELED and * desc.cb will be called in order to free desc.data. * @see eina_future_chain_easy() * @see eina_future_chain() * @see eina_future_cb_easy() * @see eina_future_chain_array() * @see eina_future_cb_easy_from_desc() */ EAPI Eina_Future *eina_future_chain_easy_array(Eina_Future *prev, const Eina_Future_Cb_Easy_Desc descs[]) EINA_ARG_NONNULL(1, 2); /** * Creates an Eina_Future_Desc that will print the previous future resolved value. * * This function can be used to quickly inspect the value that an #Eina_Future_Desc::cb * is returning. The returned value will be passed to the next future in the chain without * modifications. * * There's also a helper macro called eina_future_cb_console() which makes this * function easier to use. * * Example: * * @code * * eina_future_chain(a_future, {.cb = cb1, .data = NULL}, * //Inspect the cb1 value and pass to cb2 without modifications * eina_future_cb_console("cb1 value:"), * {.cb = cb2, .data = NULL}, * //Inspect the cb2 value * eina_future_cb_console("cb2 value:", " cb2 value suffix\n")) * @endcode * * @param[in] desc Description object with contextual information. * @return An #Eina_Future_Desc * * The description object, @p desc, can (optionally) include a prefix, suffix, * filename and function name. If these are @c NULL, empty strings ("") are used * instead. If @p desc->suffix is provided, the '\n' should be provided to ensure * the printed string ends with a line feed. * * @see eina_future_then() * @see #Eina_Future_Desc * @see eina_future_chain() * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() * @see eina_future_then_from_desc() * @see eina_future_then_easy() * @see eina_future_cb_console() * @see eina_future_cb_ignore_error() * @see eina_future_cb_log_from_desc() */ EAPI Eina_Future_Desc eina_future_cb_console_from_desc(const Eina_Future_Cb_Console_Desc desc) EINA_WARN_UNUSED_RESULT; /** * Returns a #Eina_Future_Desc that ignores an error. * * This function may be used if one wants to ignore an error. If the * specified error happens an EINA_VALUE_EMPTY will be delivered to the * next future in the chain. * * @param[in] err The error to be ignored. * @return A future descriptor to be used with eina_future_then() or eina_future_chain() */ EAPI Eina_Future_Desc eina_future_cb_ignore_error(Eina_Error err); /** * Creates an #Eina_Future_Desc which will convert the the received eina value to a given type. * * @param[in] type The Eina_Value_Type to convert to. * @return An #Eina_Future_Desc * @see eina_future_then() * @see #Eina_Future_Desc * @see eina_future_chain() * @see eina_future_cb_easy_from_desc() * @see eina_future_cb_easy() * @see eina_future_then_from_desc() * @see eina_future_then_easy() */ EAPI Eina_Future_Desc eina_future_cb_convert_to(const Eina_Value_Type *type); /** * Creates an #Eina_Future_Desc based on a #Eina_Future_Cb_Easy_Desc * * This function aims to be used in conjunction with eina_future_chain(), * eina_future_then_from_desc() and friends and its main objective is to simplify * error handling and Eina_Value type checks. * It uses three callbacks to inform the user about the future's * result and life cycle. They are: * * @li #Eina_Future_Cb_Easy_Desc::success: This callback is called when * the future execution was successful, that is, no errors occurred and * the result type matches #Eina_Future_Cb_Easy_Desc::success_type. In case * #Eina_Future_Cb_Easy_Desc::success_type is @c NULL, this function will * only be called if the future did not report an error. The value returned * by this function will be propagated to the next future in the chain (if any). * * @li #Eina_Future_Cb_Easy_Desc::error: This callback is called when * the future result is an error or #Eina_Future_Cb_Easy_Desc::success_type * does not match the future result type. The value returned * by this function will be propagated to the next future in the chain (if any). * * @li #Eina_Future_Cb_Easy_Desc::free: Called after the future was freed and any resources * allocated must be freed at this point. This callback is always called. * * Check the example below for a sample usage: * * @code * static Eina_Value * _file_size_ok(void *data, Eina_Value v) * { * Ctx *ctx = data; * // Since an Eina_Future_Cb_Easy_Desc::success_type was provided, there's no need to check the value type * int s; * eina_value_get(&v, &s); * printf("File size is %d bytes\n", s); * ctx->file_size = s; * return v; * } * * static Eina_Value * _file_size_err(void *data, Eina_Error err) * { * fprintf(stderr, "Could not read the file size. Reason: %s\n", eina_error_msg_get(err)); * // Stop propagating the error. * return EINA_VALUE_EMPTY; * } * * static void * _future_freed(void *data, const Eina_Future dead) * { * Ctx *ctx = data; * printf("Future %p deleted\n", dead); * ctx->file_size_handler_cb(ctx->file_size); * free(ctx); * } * * @code * void do_work(File_Size_Handler_Cb cb) * { * Ctx *ctx = malloc(sizeof(Ctx)); * ctx->f = get_file_size("/tmp/todo.txt"); * ctx->file_size = -1; * ctx->file_size_handler_cb = cb; * eina_future_then_easy(f, _file_size_ok, _file_size_err, _future_freed, EINA_VALUE_TYPE_INT, ctx); * } * @endcode * * @param[in] desc The easy callback's description. * @return An #Eina_Future_Desc * * @see eina_future_chain() * @see eina_future_then() * @see eina_future_cb_easy() */ EAPI Eina_Future_Desc eina_future_cb_easy_from_desc(const Eina_Future_Cb_Easy_Desc desc) EINA_WARN_UNUSED_RESULT; /** * Creates an all promise. * * Creates a promise that is resolved once all the futures * from the @p array are resolved. * The promise is resolved with an Eina_Value type array which * contains EINA_VALUE_TYPE_VALUE elements. The result array is * ordered according to the @p array argument. Example: * * @code * * static const char * * _get_operation_name_by_index(int idx) * { * switch (idx) * { * case 0: return "Get file data"; * case 1: return "Get file size"; * default: return "sum"; * } * } * * static Eina_Value * _all_cb(const void *data EINA_UNUSED, const Eina_Value array, const Eina_Future *dead EINA_UNUSED) * { * Eina_Error err; * unsigned int i, len; * * if (array.type == EINA_VALUE_TYPE_ERROR) * { * eina_value_get(&array, &err); * fprintf(stderr, "Could not complete all operations. Reason: %s\n", eina_error_msg_get(err)); * return array; * } * len = eina_value_array_count(&array); * for (i = 0; i < len; i++) * { * Eina_Value v; * eina_value_array_get(&array, i, &v); * if (v.type == EINA_VALUE_TYPE_ERROR) * { * eina_value_get(&v, &err); * fprintf(stderr, "Could not complete operation '%s'. Reason: %s\n", _get_operation_name_by_index(i), eina_error_msg_get(err)); * continue; * } * if (!i) * { * const char *msg; * if (v.type != EINA_VALUE_TYPE_STRING) * { * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_STRING->name, v.type->name); * continue; * } * eina_value_get(&v, &msg); * printf("File content:%s\n", msg); * } * else if (i == 1) * { * int i; * if (v.type != EINA_VALUE_TYPE_INT) * { * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_INT->name, v.type->name); * continue; * } * eina_value_get(&v, &i); * printf("File size: %d\n", i); * } * else * { * double p; * if (v.type != EINA_VALUE_TYPE_DOUBLE) * { * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_DOUBLE->name, v.type->name); * continue; * } * eina_value_get(&v, &p); * printf("50 places of PI: %f\n", p); * } * } * return array; * } * * void func(void) * { * Eina_Future *f1, *f2, *f3, f_all; * * f1 = read_file("/tmp/todo.txt"); * f2 = get_file_size("/tmp/file.txt"); * // calculates 50 places of PI * f3 = calc_pi(50); * f_all = eina_future_all(f1, f2, f3); * eina_future_then(f_all, _all_cb, NULL); * } * @endcode * * @param[in,out] array An array of futures, @c #EINA_FUTURE_SENTINEL terminated. * @return A promise or @c NULL on error. * @note On error all the futures will be CANCELED. * @see eina_future_all_array() */ EAPI Eina_Promise *eina_promise_all_array(Eina_Future *array[]) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; /** * Creates a race promise. * * Creates a promise that resolves when a future from the @p array * is completed. The remaining futures will be canceled with the * error code `ECANCELED` * * The resulting value is an EINA_VALUE_TYPE_STRUCT with two fields: * * @li A field named "value" which contains an Eina_Value with the result itself. * @li A field named "index" which is an int that contains the index of the completed * function relative to the @p array; * * Example. * * @code * * static const char * * _get_operation_name_by_index(int idx) * { * switch (idx) * { * case 0: return "Get file data"; * case 1: return "Get file size"; * default: return "sum"; * } * } * * static Eina_Value * _race_cb(const void *data EINA_UNUSED, const Eina_Value v) * { * unsigned int i; * Eina_Value result; * Eina_Error err; * Eina_Value_Struct *st; * * // No need to check for the 'v' type. eina_future_cb_easy() did that for us. * // However we should check if the struct has the correct description * st = eina_value_memory_get(&v); * if (st->desc != EINA_PROMISE_RACE_STRUCT_DESC) * { * fprintf(stderr, "Eina_Value is not a race struct\n"); * return v; * } * eina_value_struct_get(&v, "index", &i); * // Get the operation result * eina_value_struct_get(&v, "value", &result); * if (!i) * { * const char *msg; * if (result.type != EINA_VALUE_TYPE_STRING) * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_STRING->name, result.type->name); * else * { * eina_value_get(&result, &msg); * printf("File content:%s\n", msg); * } * } * else if (i == 1) * { * int i; * if (result.type != EINA_VALUE_TYPE_INT) * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_INT->name, v.type->name); * else * { * eina_value_get(&result, &i); * printf("File size: %d\n", i); * } * } * else * { * double p; * if (result.type != EINA_VALUE_TYPE_DOUBLE) * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_DOUBLE->name, result.type->name); * else * { * eina_value_get(&result, &p); * printf("50 places of PI: %f\n", p); * } * } * eina_value_flush(&result); * return v; * } * * static Eina_Value * _race_err(void *data, Eina_Error err) * { * fprintf(stderr, "Could not complete the race future. Reason: %s\n", eina_error_msg_get(err)); * return EINA_VALUE_EMPTY; * } * * void func(void) * { * static const *Eina_Future[] = {NULL, NULL, NULL, NULL}; * Eina_List *l = NULL; * * futures[0] = read_file("/tmp/todo.txt"); * futures[1] = get_file_size("/tmp/file.txt"); * // calculates 50 places of PI * futures[2] = calc_pi(50); * f_race = eina_future_race_array(futures); * eina_future_then_easy(f_race, _race_cb, _race_err, NULL, EINA_VALUE_TYPE_STRUCT, NULL); * } * @endcode * * @param[in,out] array An array of futures, @c #EINA_FUTURE_SENTINEL terminated. * @return A promise or @c NULL on error. * @note On error all the futures will be CANCELED. * @see eina_future_race_array() * @see #_Eina_Future_Race_Result */ EAPI Eina_Promise *eina_promise_race_array(Eina_Future *array[]) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; /** * @struct _Eina_Future_Race_Result * The struct that is used to store the race result. * * When using eina_promise_race_array() and friends, the future result * will be reported as a struct. The values can be obtained using * eina_value_struct_get() or one could access the struct directly * such as this example: * * @code * static Eina_Value * _race_cb(const void *data EINA_UNUSED, const Eina_Value v) * { * //code... * Eina_Value_Struct st; * Eina_Future_Race_Result *rr; * eina_value_get(v, &st); * rr = st.memory; * printf("Winning future index: %u\n", rr->index); * //more code.. * return v; * } * @endcode * * @see eina_promise_race_array() * @see eina_future_race_array() * @see eina_promise_race() * @see eina_future_race() * @see #EINA_PROMISE_RACE_STRUCT_DESC */ struct _Eina_Future_Race_Result { /** * The race result. */ Eina_Value value; /** * The future index that won the race. */ unsigned int index; }; /** * @var EINA_PROMISE_RACE_STRUCT_DESC * * A pointer to the race struct description, which * is used by eina_promise_race_array(); * * This struct contains two members: * @li value An EINA_VALUE_TYPE_VALUE that contains the future result that wont the race. * @li index An EINA_VALUE_TYPE_UINT that contains the future index that won the race. * * @see eina_promise_race_array() * @see #_Eina_Future_Race_Result */ EAPI extern const Eina_Value_Struct_Desc *EINA_PROMISE_RACE_STRUCT_DESC; /** * Creates a future that will be resolved once all futures from @p array is resolved. * This is a helper over eina_promise_all_array() * * @param[in,out] array A future array, must be terminated with #EINA_FUTURE_SENTINEL * @return A future. * @see eina_promise_all_array() * @see #EINA_FUTURE_SENTINEL */ static inline Eina_Future * eina_future_all_array(Eina_Future *array[]) { Eina_Promise *p = eina_promise_all_array(array); if (!p) return NULL; return eina_future_new(p); } /** * Creates a future that will be resolved once a future @p array is resolved. * This is a helper over eina_promise_race_array() * * @param[in,out] array A future array, must be terminated with #EINA_FUTURE_SENTINEL * @return A future. * @see eina_promise_race_array() * @see #EINA_FUTURE_SENTINEL */ static inline Eina_Future * eina_future_race_array(Eina_Future *array[]) { Eina_Promise *p = eina_promise_race_array(array); if (!p) return NULL; return eina_future_new(p); } /** * Used by eina_promise_race_array() and eina_promise_all_array() and * friends to flag the end of the array. * * @see eina_promise_race_array() * @see eina_promise_all_array() */ #define EINA_FUTURE_SENTINEL ((void *)(unsigned long)-1) /** * A syntactic sugar over eina_promise_race_array(). * Usage: * @code * promise = eina_promise_race(future1, future2, future3, future4); * @endcode * @see eina_promise_race_array() */ #define eina_promise_race(...) eina_promise_race_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL}) /** * A syntactic sugar over eina_future_race_array(). * Usage: * @code * future = eina_future_race(future1, future2, future3, future4); * @endcode * @see eina_future_race_array() */ #define eina_future_race(...) eina_future_race_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL}) /** * A syntactic sugar over eina_future_all_array(). * Usage: * @code * future = eina_future_all(future1, future2, future3, future4); * @endcode * @see eina_future_all_array() */ #define eina_future_all(...) eina_future_all_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL}) /** * A syntactic sugar over eina_promise_all_array(). * Usage: * @code * promise = eina_promise_all(future1, future2, future3, future4); * @endcode * @see eina_promise_all_array() */ #define eina_promise_all(...) eina_promise_all_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL}) /** * A syntactic sugar over eina_future_cb_easy_from_desc(). * Usage: * @code * future_desc = eina_future_cb_easy(_success_cb, _error_cb, _free_cb, EINA_VALUE_TYPE_INT, my_data); * @endcode * @see eina_future_cb_easy_from_desc() */ #define eina_future_cb_easy(...) eina_future_cb_easy_from_desc((Eina_Future_Cb_Easy_Desc){__VA_ARGS__}) /** * A syntactic sugar over eina_future_chain_array(). * Usage: * @code * future = eina_future_chain(future, {.cb = _my_cb, .data = my_data}, {.cb = _my_another_cb, .data = NULL}); * @endcode * @see eina_future_chain_array() */ #define eina_future_chain(_prev, ...) eina_future_chain_array(_prev, (Eina_Future_Desc[]){__VA_ARGS__, {.cb = NULL, .data = NULL}}) /** * A syntactic sugar over eina_future_then_from_desc(). * Usage: * @code * future = eina_future_then(future, _my_cb, my_data); * @endcode * @see eina_future_then_from_desc() * @see eina_future_then_easy() */ #define eina_future_then(_prev, ...) eina_future_then_from_desc(_prev, (Eina_Future_Desc){__VA_ARGS__}) /** * A syntactic sugar over eina_future_cb_console_from_desc(). * Usage: * @code * desc = eina_future_cb_console(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_console_from_desc() */ #define eina_future_cb_console(...) eina_future_cb_console_from_desc((Eina_Future_Cb_Console_Desc){__VA_ARGS__}) /** * A syntactic sugar over eina_future_cb_log_from_desc(). * * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_DBG will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * Usage: * @code * desc = eina_future_cb_log_dbg(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_log_from_desc() */ #define eina_future_cb_log_dbg(_prefix, _suffix) \ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \ __FUNCTION__, EINA_LOG_LEVEL_DBG, EINA_LOG_DOMAIN_DEFAULT, __LINE__}) /** * A syntactic sugar over eina_future_cb_log_from_desc(). * * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_CRITICAL will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * Usage: * @code * desc = eina_future_cb_log_crit(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_log_from_desc() */ #define eina_future_cb_log_crit(_prefix, _suffix) \ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \ __FUNCTION__, EINA_LOG_LEVEL_CRITICAL, EINA_LOG_DOMAIN_DEFAULT, __LINE__}) /** * A syntactic sugar over eina_future_cb_log_from_desc(). * * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_ERR will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * Usage: * @code * desc = eina_future_cb_log_err(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_log_from_desc() */ #define eina_future_cb_log_err(_prefix, _suffix) \ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \ __FUNCTION__, EINA_LOG_LEVEL_ERR, EINA_LOG_DOMAIN_DEFAULT, __LINE__}) /** * A syntactic sugar over eina_future_cb_log_from_desc(). * * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_INFO will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * Usage: * @code * desc = eina_future_cb_log_info(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_log_from_desc() */ #define eina_future_cb_log_info(_prefix, _suffix) \ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \ __FUNCTION__, EINA_LOG_LEVEL_INFO, EINA_LOG_DOMAIN_DEFAULT, __LINE__}) /** * A syntactic sugar over eina_future_cb_log_from_desc(). * * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc: * * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used. * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used. * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_WARN will be used. * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used. * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used. * * Usage: * @code * desc = eina_future_cb_log_warn(.prefix = "prefix", .suffix = "suffix"); * @endcode * @see eina_future_cb_log_from_desc() */ #define eina_future_cb_log_warn(_prefix, _suffix) \ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \ __FUNCTION__, EINA_LOG_LEVEL_WARN, EINA_LOG_DOMAIN_DEFAULT, __LINE__}) /** * A syntactic sugar over eina_future_then() and eina_future_cb_easy(). * * Usage: * @code * f = eina_future_then_easy(f, .success = _success_cb, .success_type = EINA_VALUE_TYPE_DOUBLE, .data = NULL, ); * @endcode * @see eina_future_then_from_desc() * @see eina_future_easy() * @see eina_future_then() * @see eina_future_cb_easy_from_desc() */ #define eina_future_then_easy(_prev, ...) eina_future_then_from_desc(_prev, eina_future_cb_easy(__VA_ARGS__)) /** * A syntactic sugar over eina_future_chain() and eina_future_cb_easy(). * * Usage: * @code * f = eina_future_chain_easy(f, {.success = _success_cb, .success_type = EINA_VALUE_TYPE_DOUBLE, .data = NULL}, * { .success = _success2_cb }, {.error = error_cb}); * @endcode * @see eina_future_chain_array() * @see eina_future_easy() * @see eina_future_chain_easy_array() * @see eina_future_cb_easy_from_desc() */ #define eina_future_chain_easy(_prev, ...) eina_future_chain_easy_array(_prev, (Eina_Future_Cb_Easy_Desc[]) {__VA_ARGS__, {NULL, NULL, NULL, NULL, NULL}}) /** * @} */ #ifdef __cplusplus } #endif #endif