eina: simplify and document our futures example

This commit is contained in:
Andy Williams 2017-12-13 12:23:39 +00:00
parent 54070a9226
commit af48dd8fba
1 changed files with 183 additions and 180 deletions

View File

@ -14,231 +14,234 @@
* TODO
*/
#define DEFAULT_MSG "the simple example is working!"
#define VALUE_TYPE_CHECK(_v, _type) \
if (_v.type != _type) \
{ \
fprintf(stderr, "Value type is not '%s' - received '%s'\n", \
_type->name, _v.type->name); \
return _v; \
}
typedef struct _Ctx {
Eina_Promise *p;
Eina_Bool should_fail;
Ecore_Timer *timer;
Eina_Value *value;
} Ctx;
/*
* This will be called if a promise is cancelled
*/
static void
_promise_cancel(void *data, const Eina_Promise *dead EINA_UNUSED)
_promise_cancel(void *data EINA_UNUSED, const Eina_Promise *dead EINA_UNUSED)
{
Ctx *ctx = data;
if (ctx->timer) efl_del(ctx->timer);
eina_value_free(ctx->value);
free(ctx);
printf("Promise cancelled\n");
}
static Ctx *
_promise_ctx_new(Eina_Value *v)
/*
* 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)
{
printf("Found value %s\n", eina_value_to_string(&value));
return value;
}
/*
* This method will resolve the passed promise with a string value
*/
static Eina_Value
_delayed_value_resolve(void *data, const Eina_Value value EINA_UNUSED)
{
Eina_Promise *promise;
promise = (Eina_Promise *)data;
eina_promise_resolve(promise, eina_value_string_init("Delayed Value :)"));
return EINA_VALUE_EMPTY;
}
/*
* A simple future demo, set up a promised future
* and resolve it from a timer future.
*/
static void
_simple_future()
{
Ctx *ctx;
Efl_Loop *loop;
ctx = calloc(1, sizeof(Ctx));
EINA_SAFETY_ON_NULL_GOTO(ctx, err_ctx);
Eina_Promise *promise;
// Create a demo promise for the sake of a trivial demo
loop = efl_loop_main_get(EFL_LOOP_CLASS);
ctx->p = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, ctx);
EINA_SAFETY_ON_NULL_GOTO(ctx->p, err_timer);
ctx->value = v;
return ctx;
err_timer:
free(ctx);
err_ctx:
eina_value_free(v);
return NULL;
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
eina_future_then_easy(efl_loop_timeout(efl_loop_main_get(EFL_LOOP_CLASS), 0.1),
.success = _delayed_value_resolve, .data = promise);
}
/*
* This method prints the message of the error encountered and returns no value.
*/
static Eina_Value
_error_print(void *data EINA_UNUSED, const Eina_Error error)
{
printf("Encountered error %s\n", 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;
promise = (Eina_Promise *)data;
eina_promise_reject(promise, EINA_ERROR_MAGIC_FAILED);
return EINA_VALUE_EMPTY;
}
/*
* A simple future failed demo, set up a promised future
* and reject it from a timer future.
*/
static void
_timeout(void *data, const Efl_Event *event EINA_UNUSED)
_failed_future()
{
Ctx *ctx = data;
Efl_Loop *loop;
Eina_Promise *promise;
if (ctx->should_fail)
{
eina_promise_reject(ctx->p, ENETDOWN);
}
else
{
Eina_Value v;
eina_value_copy(ctx->value, &v);
eina_promise_resolve(ctx->p, v);
eina_value_free(ctx->value);
}
// 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);
efl_del(ctx->timer);
free(ctx);
// Tis future will trigger a _value_print once resolved
eina_future_then_easy(eina_future_new(promise), .error = _error_print);
// 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.1),
.success = _delayed_value_reject, .data = promise);
}
static Eina_Future *
_future_get(Ctx *ctx)
/*
* A simple future failed demo, set up a promised future
* and reject it from a timer future.
*/
static void
_cancel_future()
{
Eina_Future *f;
Efl_Loop *loop;
Eina_Promise *promise;
Eina_Future *future;
f = eina_future_new(ctx->p);
EINA_SAFETY_ON_NULL_GOTO(f, err_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
loop = efl_loop_main_get(EFL_LOOP_CLASS);
promise = eina_promise_new(efl_loop_future_scheduler_get(loop),
_promise_cancel, NULL);
efl_add(EFL_LOOP_TIMER_CLASS, NULL,
ctx->timer = efl_added,
efl_loop_timer_interval_set(efl_added, 0.1),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _timeout, ctx));
efl_loop_timer_interval_set(efl_added, delay),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK,
_timeout, promise));
EINA_SAFETY_ON_NULL_GOTO(ctx->timer, err_timer);
return f;
err_timer:
eina_future_cancel(f);
err_future:
return NULL;
}
static Eina_Future *
_fail_future_get(void)
{
Ctx *ctx = _promise_ctx_new(NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
ctx->should_fail = EINA_TRUE;
return _future_get(ctx);
}
static Eina_Future *
_str_future_get(void)
{
Eina_Value *v = eina_value_util_string_new(DEFAULT_MSG);
EINA_SAFETY_ON_NULL_RETURN_VAL(v, NULL);
Ctx *ctx = _promise_ctx_new(v);
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
return _future_get(ctx);
return eina_future_new(promise);
}
/*
* A value callback to chain, taking in an int value and returning to chain
* the int multiplied by two.
*/
static Eina_Value
_simple_ok(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
{
VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
return v;
}
static Eina_Value
_simple_err(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
{
VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
return v;
}
static void
_simple(void)
{
eina_future_chain(_str_future_get(),
eina_future_cb_console("Expecting the following message: "DEFAULT_MSG "\n Got: ", NULL),
{ .cb = _simple_ok, .data = NULL });
eina_future_chain(_fail_future_get(),
eina_future_cb_console("Expecting network down error\n Got: ", NULL),
{ .cb = _simple_err, .data = NULL });
}
static Eina_Future *
_int_future_get(void)
{
Eina_Value *v = eina_value_util_int_new(1);
EINA_SAFETY_ON_NULL_RETURN_VAL(v, NULL);
Ctx *ctx = _promise_ctx_new(v);
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
return _future_get(ctx);
}
static Eina_Value
_chain_no_errors_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
_chain_multiply_cb(void *data EINA_UNUSED, const Eina_Value v,
const Eina_Future *dead_future EINA_UNUSED)
{
int val;
VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
eina_value_get(&v, &val);
if (v.type != EINA_VALUE_TYPE_INT)
{
fprintf(stderr, "Incorrect type was returned");
return v;
}
eina_value_get(&v, &val);
return *eina_value_util_int_new(val * 2);
}
/*
* This chained callback exits our example.
*/
static Eina_Value
_exit_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
_exit_cb(void *data EINA_UNUSED, const Eina_Value v EINA_UNUSED,
const Eina_Future *dead_future EINA_UNUSED)
{
efl_exit(0);
return v;
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
* eina_future_cb_console prints a message including the value before passing
* it on.
*/
static void
_chain_no_errors(void)
_chained_future(void)
{
eina_future_chain(_int_future_get(),
eina_future_cb_console("Expecting number 1\n Got: ", NULL),
{.cb = _chain_no_errors_cb, .data = NULL},
eina_future_cb_console("Expecting number 2\n Got: ", NULL),
{.cb = _chain_no_errors_cb, .data = NULL},
eina_future_cb_console("Expecting number 4\n Got: ", NULL),
{.cb = _chain_no_errors_cb, .data = NULL},
eina_future_cb_console("Expecting number 8\n Got: ", NULL),
{.cb = _chain_no_errors_cb, .data = NULL},
eina_future_cb_console("Expecting number 16\n Got: ", NULL),
eina_future_chain(_delayed_int_future_get(0.1),
eina_future_cb_console("Starting chain with: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _chain_multiply_cb, .data = NULL},
eina_future_cb_console(" Multiplied by 2: ", NULL),
{.cb = _exit_cb, .data = NULL});
}
static Eina_Value
_chain_with_error_cb(void *data EINA_UNUSED, const Eina_Value v EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
{
Eina_Value err;
eina_value_setup(&err, EINA_VALUE_TYPE_ERROR);
eina_value_set(&err, E2BIG);
return err;
}
static void
_chain_with_error(void)
{
eina_future_chain(_int_future_get(),
{ .cb=_chain_with_error_cb, .data=NULL },
eina_future_cb_console("Expecting argument list too long. Got: ", NULL),
{ .cb = _simple_err, .data = NULL });
}
static Eina_Value
_canceled_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
{
VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
return v;
}
static void
_future_cancel(void)
{
Eina_Future *f;
f = eina_future_chain(_int_future_get(),
eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
{ .cb = _canceled_cb, .data = NULL },
eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
{ .cb = _canceled_cb, .data = NULL },
eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL));
eina_future_cancel(f);
}
/*
* Run some futures examples.
*/
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
_simple();
_chain_no_errors();
_chain_with_error();
_future_cancel();
_simple_future();
_failed_future();
_cancel_future();
_chained_future();
}
EFL_MAIN()