forked from enlightenment/efl
eina: add eina_promise_continue_new to create a promise using an existing dead future.
This commit is contained in:
parent
0b6f74fde6
commit
79d7483fe7
|
@ -315,11 +315,15 @@ _eina_promise_cancel(Eina_Promise *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_eina_promise_value_steal_and_link(Eina_Value value, Eina_Future *f)
|
_eina_promise_value_steal_and_link(Eina_Future_Scheduler *scheduler,
|
||||||
|
Eina_Value value,
|
||||||
|
Eina_Future *f)
|
||||||
{
|
{
|
||||||
Eina_Promise *p = _eina_value_promise_steal(&value);
|
Eina_Promise *p = _eina_value_promise_steal(&value);
|
||||||
DBG("Promise %p stolen from value", p);
|
DBG("Promise %p stolen from value", p);
|
||||||
eina_value_flush(&value);
|
eina_value_flush(&value);
|
||||||
|
// In the case of continue promise, define a scheduler when steal&link
|
||||||
|
if (!p->scheduler) p->scheduler = scheduler;
|
||||||
if (f) _eina_promise_link(p, f);
|
if (f) _eina_promise_link(p, f);
|
||||||
else _eina_promise_unlink(p);
|
else _eina_promise_unlink(p);
|
||||||
}
|
}
|
||||||
|
@ -377,7 +381,7 @@ _eina_value_is(const Eina_Value v1, const Eina_Value v2)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_eina_future_dispatch(Eina_Future *f, Eina_Value value)
|
_eina_future_dispatch(Eina_Future_Scheduler *scheduler, Eina_Future *f, Eina_Value value)
|
||||||
{
|
{
|
||||||
Eina_Value next_value = _eina_future_dispatch_internal(&f, value);
|
Eina_Value next_value = _eina_future_dispatch_internal(&f, value);
|
||||||
if (!_eina_value_is(next_value, value)) eina_value_flush(&value);
|
if (!_eina_value_is(next_value, value)) eina_value_flush(&value);
|
||||||
|
@ -386,7 +390,7 @@ _eina_future_dispatch(Eina_Future *f, Eina_Value value)
|
||||||
if (next_value.type == &EINA_VALUE_TYPE_PROMISE)
|
if (next_value.type == &EINA_VALUE_TYPE_PROMISE)
|
||||||
{
|
{
|
||||||
DBG("There are no more futures, but next_value is a promise setting p->future to NULL.");
|
DBG("There are no more futures, but next_value is a promise setting p->future to NULL.");
|
||||||
_eina_promise_value_steal_and_link(next_value, NULL);
|
_eina_promise_value_steal_and_link(scheduler, next_value, NULL);
|
||||||
}
|
}
|
||||||
else eina_value_flush(&next_value);
|
else eina_value_flush(&next_value);
|
||||||
return;
|
return;
|
||||||
|
@ -401,19 +405,22 @@ _eina_future_dispatch(Eina_Future *f, Eina_Value value)
|
||||||
eina_value_pget(&next_value, &p);
|
eina_value_pget(&next_value, &p);
|
||||||
DBG("Future %p will wait for a new promise %p", f, p);
|
DBG("Future %p will wait for a new promise %p", f, p);
|
||||||
}
|
}
|
||||||
_eina_promise_value_steal_and_link(next_value, f);
|
_eina_promise_value_steal_and_link(scheduler, next_value, f);
|
||||||
}
|
}
|
||||||
else _eina_future_dispatch(f, next_value);
|
else _eina_future_dispatch(scheduler, f, next_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_scheduled_entry_cb(Eina_Future *f, Eina_Value value)
|
_scheduled_entry_cb(Eina_Future *f, Eina_Value value)
|
||||||
{
|
{
|
||||||
|
// This function is called by the scheduler, so it has to be defined
|
||||||
|
Eina_Future_Scheduler *scheduler = f->scheduled_entry->scheduler;
|
||||||
|
|
||||||
eina_lock_take(&_pending_futures_lock);
|
eina_lock_take(&_pending_futures_lock);
|
||||||
_pending_futures = eina_list_remove(_pending_futures, f);
|
_pending_futures = eina_list_remove(_pending_futures, f);
|
||||||
eina_lock_release(&_pending_futures_lock);
|
eina_lock_release(&_pending_futures_lock);
|
||||||
f->scheduled_entry = NULL;
|
f->scheduled_entry = NULL;
|
||||||
_eina_future_dispatch(f, value);
|
_eina_future_dispatch(scheduler, f, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -471,6 +478,11 @@ _eina_future_schedule(Eina_Promise *p,
|
||||||
Eina_Future *f,
|
Eina_Future *f,
|
||||||
Eina_Value value)
|
Eina_Value value)
|
||||||
{
|
{
|
||||||
|
if (!p->scheduler)
|
||||||
|
{
|
||||||
|
ERR("Trying to resolve a continue promise during a future callback. Directly return Eina_Value instead.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
f->scheduled_entry = p->scheduler->schedule(p->scheduler,
|
f->scheduled_entry = p->scheduler->schedule(p->scheduler,
|
||||||
_scheduled_entry_cb,
|
_scheduled_entry_cb,
|
||||||
f, value);
|
f, value);
|
||||||
|
@ -495,7 +507,8 @@ _eina_promise_deliver(Eina_Promise *p,
|
||||||
{
|
{
|
||||||
Eina_Future *f = p->future;
|
Eina_Future *f = p->future;
|
||||||
_eina_promise_unlink(p);
|
_eina_promise_unlink(p);
|
||||||
if (value.type == &EINA_VALUE_TYPE_PROMISE) _eina_promise_value_steal_and_link(value, f);
|
if (value.type == &EINA_VALUE_TYPE_PROMISE)
|
||||||
|
_eina_promise_value_steal_and_link(p->scheduler, value, f);
|
||||||
else _eina_future_schedule(p, f, value);
|
else _eina_future_schedule(p, f, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -598,7 +611,8 @@ _eina_promise_clean_dispatch(Eina_Promise *p, Eina_Value v)
|
||||||
{
|
{
|
||||||
_eina_promise_value_dbg("Clean contenxt - Resolving promise", p, v);
|
_eina_promise_value_dbg("Clean contenxt - Resolving promise", p, v);
|
||||||
_eina_promise_unlink(p);
|
_eina_promise_unlink(p);
|
||||||
_eina_future_dispatch(f, v);
|
// This function is called on a promise created with a scheduler, not a continue one.
|
||||||
|
_eina_future_dispatch(p->scheduler, f, v);
|
||||||
}
|
}
|
||||||
eina_mempool_free(_promise_mp, p);
|
eina_mempool_free(_promise_mp, p);
|
||||||
}
|
}
|
||||||
|
@ -693,6 +707,25 @@ eina_promise_new(Eina_Future_Scheduler *scheduler,
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EAPI Eina_Promise *
|
||||||
|
eina_promise_continue_new(const Eina_Future *dead_future,
|
||||||
|
Eina_Promise_Cancel_Cb cancel_cb, const void *data)
|
||||||
|
{
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cancel_cb, NULL);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dead_future, NULL);
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(dead_future->scheduled_entry != NULL, NULL);
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL(dead_future->promise != NULL, NULL);
|
||||||
|
|
||||||
|
Eina_Promise *p = eina_mempool_calloc(_promise_mp, sizeof(Eina_Promise));
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
|
||||||
|
p->cancel = cancel_cb;
|
||||||
|
p->data = data;
|
||||||
|
p->scheduler = NULL;
|
||||||
|
DBG("Creating continuing new promise - Promise:%p, cb: %p, data:%p", p,
|
||||||
|
p->cancel, p->data);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
EAPI void
|
EAPI void
|
||||||
eina_future_cancel(Eina_Future *f)
|
eina_future_cancel(Eina_Future *f)
|
||||||
{
|
{
|
||||||
|
|
|
@ -531,6 +531,7 @@ struct _Eina_Future_Desc {
|
||||||
* @return A promise or @c NULL on error.
|
* @return A promise or @c NULL on error.
|
||||||
* @see eina_future_cancel()
|
* @see eina_future_cancel()
|
||||||
* @see eina_future_new()
|
* @see eina_future_new()
|
||||||
|
* @see eina_promise_continue_new()
|
||||||
* @see eina_promise_resolve()
|
* @see eina_promise_resolve()
|
||||||
* @see eina_promise_reject()
|
* @see eina_promise_reject()
|
||||||
* @see eina_promise_data_get()
|
* @see eina_promise_data_get()
|
||||||
|
@ -542,6 +543,72 @@ struct _Eina_Future_Desc {
|
||||||
*/
|
*/
|
||||||
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;
|
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, cancelling 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, cancelling all pending futures (ie: 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 indirectaly (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 dead_future The future being resolved to get a scheduler from.
|
||||||
|
* @param 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 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_data_get()
|
||||||
|
* @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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the data attached to the promise.
|
* Gets the data attached to the promise.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue