Overhaul of the Eina_Future example

Run tests consecutively, triggering next one on the future's free callback.
Label test numbers explicitly, code reordering and beautifying.
Some console output beautifying too.
This commit is contained in:
Xavi Artigas 2018-01-02 16:26:03 +01:00
parent 348ea10272
commit 11091214f7
1 changed files with 192 additions and 143 deletions

View File

@ -9,67 +9,42 @@
#include <Efl_Core.h>
/*
* Eina Future examples.
*
* TODO
* A collection of Eina Future examples.
* TEST 1: Set up a promised future and resolve it from a timer callback.
* TEST 2: Set up a promised future and reject it from a timer callback.
* TEST 3: Set up a promised future and then cancel it.
* TEST 4: Set up a chain of futures that get called one after the other when
* the promise is resolved, passing a value among them.
*/
/*
* This will be called if a promise is cancelled
*/
static void
_promise_cancel(void *data EINA_UNUSED, const Eina_Promise *dead EINA_UNUSED)
{
printf("Promise cancelled\n");
}
static void _test1_simple_future();
static void _test2_failed_future();
static void _test3_cancelled_future();
static void _test4_chained_future();
/* ----------------------- Generic helper methods ---------------------- */
/*
* This simple method prints the content of a value and passes it on
* This simple method prints the content of a value and passes it on.
*/
static Eina_Value
_value_print(void *data EINA_UNUSED, const Eina_Value value)
_value_print(void *data, const Eina_Value value)
{
printf("Found value %s\n", eina_value_to_string(&value));
Eina_Promise *promise = (Eina_Promise *)data;
char *str = eina_value_to_string(&value);
printf(" Success callback: Promise %p resolved to '%s'\n", promise, str);
free(str);
return value;
}
/*
* This method will resolve the passed promise with a string value
* This will be called if a promise is cancelled.
*/
static void
_delayed_value_resolve(void *data, const Efl_Event *event)
_promise_cancel(void *data EINA_UNUSED, const Eina_Promise *dead EINA_UNUSED)
{
Eina_Promise *promise;
promise = (Eina_Promise *)data;
eina_promise_resolve(promise, eina_value_string_init("Delayed Value :)"));
efl_del(event->object);
}
/*
* A simple future demo, set up a promised future
* and resolve it from a timer future.
*/
static void
_simple_future()
{
Efl_Loop *loop;
Eina_Promise *promise;
// Create a demo promise for the sake of a trivial demo
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop), _promise_cancel, NULL);
// Tis future will trigger a _value_print once resolved
eina_future_then_easy(eina_future_new(promise), .success = _value_print);
// This future is basically a timer - wait 100ms and then resolve the promise above
efl_add(EFL_LOOP_TIMER_CLASS, loop,
efl_loop_timer_interval_set(efl_added, 0.1),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_delayed_value_resolve, promise));
printf(" Promise cancellation callback: Promise %p cancelled\n", dead);
}
/*
@ -78,105 +53,162 @@ _simple_future()
static Eina_Value
_error_print(void *data EINA_UNUSED, const Eina_Error error)
{
printf("Encountered error %s\n", eina_error_msg_get(error));
Eina_Promise *promise = (Eina_Promise *)data;
printf(" Error callback: Promise %p encountered error '%s'\n", promise,
eina_error_msg_get(error));
return EINA_VALUE_EMPTY;
}
/*
* This method will reject the passed promise with a "magic" error.
*/
static Eina_Value
_delayed_value_reject(void *data, const Eina_Value value EINA_UNUSED)
{
Eina_Promise *promise;
/* ----------------------- Test 1 ---------------------- */
promise = (Eina_Promise *)data;
eina_promise_reject(promise, EINA_ERROR_NOT_IMPLEMENTED);
return EINA_VALUE_EMPTY;
}
/*
* A simple future failed demo, set up a promised future
* and reject it from a timer future.
*/
/* Resolve the promise passed through data to a string and cancel the timer */
static void
_failed_future()
_test1_resolve(void *data, const Efl_Event *event)
{
Eina_Promise *promise = (Eina_Promise *)data;
eina_promise_resolve(promise, eina_value_string_init("Test 1 Delayed Value"));
efl_del(event->object);
}
/* Launches the next test */
static void
_test1_next_test(void *data EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
{
printf(" Test 1 finished\n");
_test2_failed_future();
}
/* TEST 1: Set up a promised future and resolve it from a timer callback */
static void
_test1_simple_future()
{
Efl_Loop *loop;
Eina_Promise *promise;
// Create a demo promise for the sake of a trivial demo
// Create a promise
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop), _promise_cancel, NULL);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, NULL);
printf("Test 1: Waiting for promise %p to be resolved...\n", promise);
// Tis future will trigger a _value_print once resolved
eina_future_then_easy(eina_future_new(promise), .error = _error_print);
// This future will call _value_print() when the promise is resolved and
// execute the next test when the future is destroyed (for whatever reason)
eina_future_then_easy(eina_future_new(promise),
.success = _value_print,
.free = _test1_next_test,
.data = promise);
// This future is basically a timer - wait 100ms and then resolve the promise above
eina_future_then_easy(efl_loop_timeout(efl_loop_main_get(EFL_LOOP_CLASS), 0.2),
.success = _delayed_value_reject, .data = promise);
// Set up a regular timer that will trigger after 1s. We use it to
// simulate a delayed promise resolve.
efl_add(EFL_LOOP_TIMER_CLASS, loop,
efl_loop_timer_interval_set(efl_added, 1),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_test1_resolve, promise));
}
/*
* A simple future failed demo, set up a promised future
* and reject it from a timer future.
*/
/* ----------------------- Test 2 ---------------------- */
/* Reject the passed promise with an specific error and cancel the timer */
static void
_cancel_future()
_test2_reject(void *data, const Efl_Event *event)
{
Eina_Promise *promise = (Eina_Promise *)data;
eina_promise_reject(promise, EINA_ERROR_NOT_IMPLEMENTED);
efl_del(event->object);
}
/* Launches the next test */
static void
_test2_next_test(void *data EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
{
printf(" Test 2 finished\n");
_test3_cancelled_future();
}
/* TEST 2: Set up a promised future and reject it from a timer callback */
static void
_test2_failed_future()
{
Efl_Loop *loop;
Eina_Promise *promise;
// Create a promise
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, NULL);
printf("Test 2: Waiting for promise %p to be rejected...\n", promise);
// This future will call _value_print() when the promise is resolved.
// If there is an error, _error_print() is called instead.
// It then executes the next test when the future is destroyed.
eina_future_then_easy(eina_future_new(promise),
.success = _value_print,
.error = _error_print,
.free = _test2_next_test,
.data = promise);
// Set up a regular timer that will trigger after 1s. We use it to
// simulate a delayed promise rejection.
efl_add(EFL_LOOP_TIMER_CLASS, loop,
efl_loop_timer_interval_set(efl_added, 1),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_test2_reject, promise));
}
/* ----------------------- Test 3 ---------------------- */
/* Launches the next test */
static void
_test3_next_test(void *data EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
{
printf(" Test 3 finished\n");
_test4_chained_future();
}
/* TEST 3: Set up a promised future and then cancel it */
static void
_test3_cancelled_future()
{
Efl_Loop *loop;
Eina_Promise *promise;
Eina_Future *future;
// Create a demo promise for the sake of a trivial demo
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop), _promise_cancel, NULL);
future = eina_future_new(promise);
// Tis future will trigger a _value_print once resolved
eina_future_then_easy(future, .success = _value_print);
// Then we cancel the future before it has a chance to resolve
eina_future_cancel(future);
}
/*
* When our timeout is triggered we will resolve the promise passed.
* Set an int value to initialise the chain.
*/
static void
_timeout(void *data, const Efl_Event *event)
{
Eina_Promise *promise;
promise = data;
eina_promise_resolve(promise, eina_value_int_init(1));
efl_del(event->object);
}
/*
* Create a new int future that will resolve after a specified timer delay.
*/
static Eina_Future *
_delayed_int_future_get(double delay)
{
Efl_Loop *loop;
Eina_Promise *promise;
// Create a demo promise for the sake of a trivial demo
// Create a promise
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, NULL);
future = eina_future_new(promise);
printf("Test 3: Waiting for promise %p to be cancelled...\n", promise);
efl_add(EFL_LOOP_TIMER_CLASS, loop,
efl_loop_timer_interval_set(efl_added, delay),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_timeout, promise));
// This future will call _value_print() when the promise is resolved.
// If there is an error, _error_print() is called instead.
// It then executes the next test when the future is destroyed.
eina_future_then_easy(future,
.success = _value_print,
.error = _error_print,
.free = _test3_next_test,
.data = promise);
return eina_future_new(promise);
// Cancel the future before it has a chance to resolve
eina_future_cancel(future);
}
/* ----------------------- Test 4 ---------------------- */
/* Resolve the passed promise to an integer with value 1 and cancel the timer */
static void
_test4_resolve(void *data, const Efl_Event *event)
{
Eina_Promise *promise = (Eina_Promise *)data;
eina_promise_resolve(promise, eina_value_int_init(1));
efl_del(event->object);
}
/*
@ -184,45 +216,58 @@ _delayed_int_future_get(double delay)
* the int multiplied by two.
*/
static Eina_Value
_chain_multiply_cb(void *data EINA_UNUSED, const Eina_Value v,
_chain_multiply_cb(void *data EINA_UNUSED, const Eina_Value value,
const Eina_Future *dead_future EINA_UNUSED)
{
int val;
int i;
if (v.type != EINA_VALUE_TYPE_INT)
if (value.type != EINA_VALUE_TYPE_INT)
{
fprintf(stderr, "Incorrect type was returned");
return v;
fprintf(stderr, "Expected type 'int', got '%s'\n", value.type->name);
return value;
}
eina_value_get(&v, &val);
return *eina_value_util_int_new(val * 2);
eina_value_get(&value, &i);
return *eina_value_util_int_new(i * 2);
}
/*
* This chained callback exits our example.
*/
/* This chained callback exits the example */
static Eina_Value
_exit_cb(void *data EINA_UNUSED, const Eina_Value v EINA_UNUSED,
_exit_cb(void *data EINA_UNUSED, const Eina_Value value EINA_UNUSED,
const Eina_Future *dead_future EINA_UNUSED)
{
efl_exit(0);
printf(" Test 4 finished\n");
return EINA_VALUE_EMPTY;
}
/*
* Run a future chain where the initial future returns a single value.
* Each item in the chain then processes this value and passes on another.
* The _chain_multiply_cb returns twice the value it gets and the
* TEST 4: Run a future chain where the initial future returns a single value.
* Each item in the chain then processes this value and passes it to the next item.
* The _chain_multiply_cb() returns twice the value it gets and the
* eina_future_cb_console prints a message including the value before passing
* it on.
*/
static void
_chained_future(void)
_test4_chained_future(void)
{
eina_future_chain(_delayed_int_future_get(0.5),
eina_future_cb_console("Starting chain with: ", NULL),
Efl_Loop *loop;
Eina_Promise *promise;
// Create a promise
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, NULL);
printf("Test 4: Waiting for promise %p to be resolved...\n", promise);
// All these methods, eina_future_cb_console() and _chain_multiply_cb(), will
// be called one after the other when the promise is resolved.
// Note how the last one is _exit_cb().
// There are 10 futures chained in total.
eina_future_chain(eina_future_new(promise),
eina_future_cb_console(" Starting chain with value: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
@ -232,18 +277,22 @@ _chained_future(void)
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _exit_cb, .data = NULL});
// Set up a regular timer that will trigger after 1s. We use it to
// simulate a delayed promise resolve.
efl_add(EFL_LOOP_TIMER_CLASS, loop,
efl_loop_timer_interval_set(efl_added, 1),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_test4_resolve, promise));
}
/*
* Run some futures examples.
*/
/* ----------------------- Main ---------------------- */
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
_simple_future();
_failed_future();
_cancel_future();
_chained_future();
// Start the first test, the others will be launched when this one finishes
_test1_simple_future();
}
EFL_MAIN()