efl/src/lib/eina/eina_promise.h

1802 lines
65 KiB
C

#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 <Ecore.h>
*
* 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