From 11091214f782ccb5f9bed53ca83deccda1626ccc Mon Sep 17 00:00:00 2001 From: Xavi Artigas Date: Tue, 2 Jan 2018 16:26:03 +0100 Subject: [PATCH] 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. --- reference/c/eina/src/eina_future.c | 335 +++++++++++++++++------------ 1 file changed, 192 insertions(+), 143 deletions(-) diff --git a/reference/c/eina/src/eina_future.c b/reference/c/eina/src/eina_future.c index 10c52fa2..55f6ebf8 100644 --- a/reference/c/eina/src/eina_future.c +++ b/reference/c/eina/src/eina_future.c @@ -9,67 +9,42 @@ #include /* - * 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()