On Wed, Sep 6, 2017 at 4:33 AM, Cedric Bail <[email protected]> wrote:
>> -------- Original Message --------
>> Subject: [E-devel] Proposal: automatic Event->Promise/Future
>> Local Time: September 5, 2017 6:20 AM
>> UTC Time: September 5, 2017 1:20 PM
>> From: [email protected]
>> To: edevel <[email protected]>
>>
>> In many cases we use events as a single shot event, then we
>> efl_event_callback_add(), wait for the callback, then
>> efl_event_callback_del()...
>>
>> With the introduction of Promise/Future pair it would be nice to have
>> that automated, for example Efl.Io.Copier produces an event "done".
>> Most of the times when you"re using Efl.Io.Copier you only want to
>> know when it was finished or when it produced an error (event:
>> "error").
>>
>> Then it would improve usability to do something like:
>>
>> Eina_Future *f = efl_future_event(copier,
>> EFL_IO_COPIER_EVENT_DONE, /* success event */
>> EFL_IO_COPIER_EVENT_ERROR); /* error event */
>> eina_future_chain(f, do_something, do_something_else, on_error);
>>
>> and all would be handled for you: add/del callbacks, resolve the
>> promise and all.
>
> Just a potential idea here, why not reverse the problem. Instead of 
> implementing this future on top of this events, why not have a future and 
> have this events automatically create a future when someone register a 
> callback on it. This avoid completely the problem of dealing with the void* 
> and seems really simple to me.

I don't think this is worth due multiple reasons, immediately comes to mind:

  - huge existing code base;
  - not optimal for recurring events (ie: progress monitoring).
  - events that need immediate dispatch, instead of delayed to
"clean/safe" context as done by future;
  - due delayed approach, needs copy/ref - can't be done with on-stack
values as done for events.

Marcel pointed out that we may even want to introduce some annotation
to events of some resources, such as Efl.Object, to note if the object
should be efl_ref() by a promise or just efl_wref()... Cases:
  - efl.net.server will emit "client,add" with clients, if you do not
efl_ref(), client is rejected/discarded;
  - efl.canvas may emit some child object changed, you're not supposed
to extend its lifetime, so efl_wref is to be used...

So the first would map to EINA_VALUE_TYPE_OBJECT as now, likely we'll
introduce a new EINA_VALUE_TYPE_WOBJECT or something like that, that
will wref and null the pointer if the object dies.


>> This is doable, EXCEPT events are "void *" and may go away when the
>> event callback returns. However eina_promise_resolve() will postpone
>> the future chain to another mainloop iteration -- thus needs a COPY.
>> How to copy "void *"?
>
> Filling the Eina_Value_Type from the eolian type seems quite complex to me.

It's not complex, it's boring repetitive task, not something for
humans, something perfect for machines/generators.


> It would require to give a function to do the copy and destruction of the 
> structure in eolian most likely.

This is what Eina_Value_Type does for Eina_Value contents itself
https://git.enlightenment.org/core/efl.git/tree/src/lib/eina/eina_value.h#n3454,
or EINA_VALUE_TYPE_STRUCT provides as "ops"
https://git.enlightenment.org/core/efl.git/tree/src/lib/eina/eina_value.h#n2884
that allows you to override defaults (malloc/free...).

Then if you want to use mempool instead of malloc, you just provide
those. For usual malloc/free, do not provide anything else. For most
structures composed of existing Eina_Value_Type (native or foreign, as
defined by other eolian files), there is nothing to be done.

Anyway, as I wrote in my email it's possible, but very boring and
error prone by a human, very simple by a machine/generator.


> Also I am not sure it would work in all case, some object that we put in the 
> structure are really build to only be alive for the duration of the callback 
> (I am thinking about input event here).

If input can be duplicated, then doable (create
EINA_VALUE_TYPE_EFL_INPUT), that will provide its own setup, flush,
copy, getter, setter and even conversion to other types (ie: to
string).

If input can't be duplicated, then my proposal is that it goes as
empty. The future will get EINA_VALUE_EMPTY (all zero struct), you can
"then()" but won't be able to process value contents.

We're not converting all events to future, just making easier to
process those that are commonly used for one-shot, like Efl.Io.Copier
"done" + "error". We can leave the creation to user, by calling the
generic backing function, but we can also provide something that helps
the user, like:

    Eina_Future *efl_io_copier_done_job(Eo *o) {
        return efl_event_future_new(o, EFL_IO_COPIER_EVENT_DONE,
EFL_IO_COPIER_EVENT_ERROR);
    }

Then users will get something like:

   eina_future_chain(efl_io_copier_done_job(copier),
      { .cb = update_ui },
      { .cb = cleanup_resources });

or using "easy" variant, that adds small overhead to auto-check types
and split functions (success, error, free):

   eina_future_easy_chain(efl_io_copier_done_job(copier),
      { .success = update_ui_success, .error = update_ui_error, .data
= bla, .free = free_bla },
      { .free = cleanup_resources, .data = ble });

or using "Efl.Object"-bound variant, that binds the future lifecycle
to another object (my_obj), if it dies, then the future is
automatically cancelled. Otherwise it looks like "easy" version:

   efl_future_chain(my_obj, efl_io_copier_done_job(copier),
      { .success = update_ui_success, .error = update_ui_error, .data
= bla, .free = free_bla },
      { .free = cleanup_resources, .data = ble });

Which are very handy. Another use case is internally in Efl.Io.Copier
and users of Efl.Io.Reader where I stop listening to can_read until
the buffer drains. There I could use a "can_read_job()" rather than
manually enable/disable the event callback.

Also, as you see these "jobs" (or whatever we want to use as term) is
easy to automate, all you need is a success event and a failure event
(optional), so some new block like this would work:

    future_jobs { // name -> success_event failure_event
       done: Efl.Io.Copier,done Efl.Io.Copier,error; // optional
namespace to allow using events from parents...
       error: Efl.Io.Copier,error; // just error
       line: Efl.Io.Copier,line Efl.Io.Copier,error; // this one would
copy lines, not that optimal...
    }

Failure event would check for value type EINA_VALUE_TYPE_ERROR, if not
then it will generate a generic error (convert all payload to error
like EIO)

Cheers,


-- 
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to