While Eina_Coro provides a solid base, to use the main loop to
schedule coroutines it needs some manual work we want to avoid.
Efl.Loop.coro method will take a function and schedule it using the
given priority, the returned value is then resolved in a promise for
the returned future.
Basically all users must do is write a function that looks like a
synchronous code and calls eina_coro_yield() (or helper macros), that
will go back to the main loop and then it will reschedule the
coroutine to run according to its priority.
This should reduce the number of callbacks in user's code.
Currently Eina_Coro is implemented with actual threads, that barf Eo
checkings and makes main thread objects unusuable.
Since this is an implementation detail, let's use Eina_Coro hooks to
adopt the main domain and return when it's done... The user doesn't
see this, it's transparent.
Coroutines are cooperative tasks, in the sense that the caller will
stop until the target function runs. The target function must either
yield control back to the caller (main thread), or exit. There is no
preemption of the two tasks, thus no special care needs to be taken
regarding shared data.
If the target coroutine yields control using eina_coro_yield(), then
it will be paused until it's manually ran again by caller (main
thread), which is executed with eina_coro_run().
Another common usage is to await for another task to be completed,
this can be done by waiting for a future to be resolved. It will
automatically yield and inform the caller of the future so it can
schedule properly instead of keep calling the task. Waiting for many
tasks can be achieved by using eina_future_all() or
eina_future_race(). This is done with eina_coro_await().
Due portability it was implemented using Eina_Thread, Eina_Lock and
Eina_Condition. Regular threads will ensure that the state is fully
preserved (stack, registers) in a platform independent way. Each
thread will wait on its own turn using the Eina_Lock and
Eina_Condition, thus it's guaranteed that only one is being executed
at the same time.
The API is small and should allow different implementations shall we
need them, like manually saving the stack and registers, then
restoring those -- problem is doing that in a portable way,
setjmp()/longjmp() won't save the stack, makecontext()/swapcontext()
doesn't work right on MacOS...
Hooks can be used to be informed when the main routine exits and then
enters, likewise when the coroutine enters and exits. These will be
used, for instance, to automatically get, adopt and return
Efl_Domain_Data needed to make Efl_Object work in such
environment. The flow is simple:
- main exit (called from main thread)
- coroutine enter (called from worker thread)
- coroutine exit (called from worker thread)
- main enter (called from main thead)
Performance may not be optimal, however this is meant as easy-to-use
and it shouldn't be an issue in real life. It will be mostly exposed
in two layers:
- Efl.Loop.coro: will wrap eina_coro and and schedule using its main
loop instance, returns an Eina_Future so it's easy to chain.
- Eina_Promise/Eina_Future "async/await"-like behavior: will allow to
write "synchronous" code that can wait for promises to be
resolved. When eina_future_await(), it will actually register a new
Eina_Future in the chain and then eina_coro_yield(). Once the
future is called back it will call eina_coro_run() and allow the
coroutine to resume. This is done on top fo eina_coro_await().
This is a helper that creates a promise, then a future and immediately
resolves the promise, this will let the future be dispatched as usual,
from a clean main loop context.
_eina_promise_clean_dispatch() must call _eina_future_dispatch() since
complex chains will need to be walked -- this bugged me for some
complex tests I'm doing with coroutines.
Unfortunately that will modify the value and cause problems since the
caller trust it wasn't modified and could cause double-free or invalid
memory access. Then we must copy it in _future_proxy().
Other usage for _eina_promise_clean_dispatch() were leaking and are
now "auto fixed".
Summary:
Eina: Add Eina_Promise/Eina_Future.
This commit adds a new promise/future API which aims to replace
efl_future.
Efl_Object: Add integration with Eina_Future.
This commit adds the EO support for the new future infra.
From now on there's no need to efl_future_link()/efl_future_unlink()
object and futures since the new API already handles that internally.
Eina_Promise/Eina_Future: Add example and tests.
Subscribers: cedric, jpeg
Differential Revision: https://phab.enlightenment.org/D5131
This commit adds the EO support for the new future infra.
From now on there's no need to efl_future_link()/efl_future_unlink()
object and futures since the new API already handles that internally.
a nested compositor will have a mismatch between canvas seat id and
compositor seat id, so this attempts to perform matching based on the
order that they are listed, which should be identical
@fix
gl_generic_context_find() returns the gl shared context struct but
this is not just a read-only operation. It in turn calls window_use
which may call make_current. This can invalidate the work of evas gl
when the API tried to switch to a specific context.
This fixes evas gl with multiple outputs.
Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
For now, do not use Evas_Canvas3D in multi output context, it won't work.
The update code for Evas_Canvas3D_Node might trigger rendering logic, which
is opposite to what the scene graph logic should do. It require to much
reshuffle around to handle that case at the moment. So I am just adding a
warning.