On Wed, Jan 3, 2018 at 1:07 PM, Andrew Williams <a...@andywilliams.me> wrote:
> Then surely the we are missing something in eina to get the main loop
> scheduler?
> Moving this code into the efl namespace seems misleading if the intent is
> that it would not be part of our bindings.
> Eina is, if I understand it, there for the C implementation specifics...

answered in IRC, but doing again so it's archived together with this email:

according to the promise/future specifications, the future callback
must be dispatched "from a clean context", that is, when there is only
"platform code running", in our case, when using the main loop: it's
straight from the main loop.

in JavaScript, people often implement that using setTimeout(0,
futureDispatcher).

in our case, it is (was?) implemented using ecore_event, such as other
primitives (ecore_job, etc).

however, since eina knows nothing about ecore/main loop, we can't find
a main loop and register those in order to get the clean context to
dispatch the future callback.

to address that, Eina_Future_Scheduler exists. It is the structure
that must be filled and given to eina_promise_new() so the promise and
associated future chain uses when a future must be dispatched from
that clean context. It's very similar to "ecore_event_add()"
(Eina_Future_Scheduler->schedule()) and "ecore_event_del()"
(Eina_Future_Scheduler->recall()).

Since this is tied to a main loop instance, it's bound to Efl.Loop,
which keeps a per-instance singleton that can be retrieved with
Efl.Loop.future_scheduler_get(). This is a method since it's touching
the object internals, which register the pending futures, destroying
them if the main loop finishes (loop is deleted).

However this method was not exposed in efl_loop.eo because it was
meant to be "C-only", that is, shouldn't be automatically exposed by
bindings that uses eolian to generate wrappers.

There are no method attributes to flag methods as "C-only". To achieve
this effect other EFL, including Eo itself, declares stuff in ".h" but
not ".eo" files, see
https://git.enlightenment.org/core/efl.git/tree/src/lib/eo/Eo.h

C users have no way, they must use this method
(efl_loop_future_scheduler_get()) and eina_promise_new() directly, if
they want to create new promises.

--> Note it's not that common to create new promises! Usually you
chain from one (ie: efl_loop_timeout(), efl_loop_idle(),
efl_loop_job()...) just adding new futures to its execution chain. In
other circumstances you want to just dispatch a known value or error
using the promise/future infrastructure, in these cases you use
eina_future_resolved(scheduler, value) or
eina_future_rejected(scheduler, error).


Bindings, on the other hand, have two options:

-> Languages with native Promise, or with a common/defacto standard
library that implements one, like JavaScript/nodejs, Python, C#, C++

EFL developers using such languages shouldn't create Eina_Promise or
Eina_Future, instead they should transparently use their
language-native implementation which should transparently integrate
with EFL.

On these bindings, things like efl.loop.timeout() shouldn't return
Eina.Future direct/specific wrappers. Instead they should return
native "Promise" (or Future, but most implementations call it
"Promise", like in JavaScript, A+/Promise...), which is itself
resolved from eina_future.

Consider the following PSEUDO code that a JavaScript/EFL binding would
generate for efl_loop_timeout, which returns an Eina_Future:

    static Eina_Value
    _future_void_resolved(void *data, const Eina_Value v, const
Eina_Future *dead_future EINA_UNUSED)
    {
        JS_Obj *js_promise = data;
        if (v.type == EINA_VALUE_TYPE_ERROR)
          {
            Eina_Error err;
            eina_value_error_get(&v, &err);
            js_promise_reject(js_promise, js_error_from_eina_error(err));
          }
        else
           js_promise_resolve(js_promise, JS_Obj_Void);
      return v;
    }

    JS_Obj *
    efl_loop_timeout_wrapper(JS_Obj *wrapper, double timeout)
    {
        Efl_Loop *loop = js_obj_native_object_get(wrapper);
        Eina_Future *ef = efl_loop_timeout(loop, timeout);

        JS_Obj *js_promise = js_promise_new();
        eina_future_then(ef, _future_void_resolved, js_promise);
        return js_promise;
    }


That's why once old "Efl_Future" (Eo-classes) are removed q66 will
change eolian to emit "Eina_Future*" for the reserved keyword
"future<type>", and bindings should provide a similar tooling.

For these cases there should be no reason to expose
efl_loop_future_scheduler_get().


-> Languages without Promise and no major library/framework providing one

For these, efl_loop_future_scheduler_get() must be exposed alongside
at least one new type: Eina.Future wrapper.

If the language supports closure/context, then there is no need to
export Eina.Promise, just do something like JavaScript, using the
signature:

    new Promise(loop, callback(onResolved, onRejected));

to be used as:

    function cb(onResolved, onRejected) {
       try {
          var value = doSomeWork();
          onResolved(value);
       } catch (e) {
          onRejected(e);
       }
    }

    return new Promise(loop, cb);


in the example above, the Promise constructor implemented in C would
be calling 2 EFL functions:
   - efl_loop_future_scheduler_get()
   - eina_promise_new()

And there is no need to expose efl_loop_future_scheduler_get() at all.

-- 
Gustavo Sverzut Barbieri
--------------------------------------
Mobile: +55 (16) 99354-9890

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to