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
