On Fri, 9 Mar 2018 10:52:59 -0300 Gustavo Sverzut Barbieri <barbi...@gmail.com> said:
> On Thu, Mar 8, 2018 at 4:58 AM, Carsten Haitzler <ras...@rasterman.com> wrote: > > On Thu, 1 Mar 2018 14:11:26 -0300 Gustavo Sverzut Barbieri > > <barbi...@gmail.com> said: > > > >> On Wed, Feb 28, 2018 at 2:33 AM, Carsten Haitzler <ras...@rasterman.com> > >> wrote: > >> > On Tue, 27 Feb 2018 18:34:59 -0500 Cedric Bail <ced...@ddlm.me> said: > >> > > >> >> -------- Original Message -------- > >> >> On February 27, 2018 2:52 PM, Carsten Haitzler <ras...@rasterman.com> > >> >> wrote: > >> >> > >> >> >so I'm implementing a new efl.exe class (and efl.task and some others) > >> >> >and i > >> >> > WAS going to use a future as the return for run() ... but after just > >> >> > writing about 20 lines of code (get a scheduler, create a promise > >> >> > alloc promise data and all the checking in between) and i then > >> >> > realized... it's ballooning to an insane amount of code vs > >> >> > event_callback_call which is a 1 line func call when the event > >> >> > happens. > >> >> > > >> >> > let me just copy and paste the relevant lines i had sketched out (was > >> >> > not final or even compiling yet): > >> >> > > >> >> > typedef struct _Efl_Exe_Run_Data > >> >> > { > >> >> > Eo *obj; > >> >> > } Efl_Exe_Run_Data; > >> > >> not sure you need this > > > > i need to report the object the future is for with the future... and i don't > > want to rely on people having to use the data ptr for that. this is probably > > one of the most common needs/uses of a future - that it's a one off action > > FOR an object. > > well, the thing is: promise is about a value, not anything else... if > we start to misuse, it start to become less useful. actually it's about calling success or failure at some point in the future and standardizing this so you can have those all/race things and chain a then b then c etc. value is actually secondary to this. > for instance, with events rarely we carry payload info, usually one > queries from the object... like text changed on widgets. > > however for promises, with the chaining idea, rarely you should be > getting the owner object... in the rare cases you need it, use "the > closure" (void *data), which BTW is specific for each future in the > chain, then you can chain multiple and "pass thru" the value if you > need multiple objects (ie: say you need to get a value and show it to > 2 label objects, you can connect 2 cb in a chain, each with a label > object. actually it's going to be incredibly common. example: file_set on an image object... what do you think people want to do? they want to then SHOW the object or emit a signal to it or something. they want to initiate some response. for exe's and threads the object will not be magically deleted if the exe exits or thread exits. the object has to exist firstly to collect the results (i/o, exit code results etc.) - it has fd's it has to listen on etc. etc. ... if the object is deleted the promise cannot "continue" and report success or failure. it won't know. all tracking of the child is deleted. i have chosen for now at least that deletion doesn't block and wait for a child to exit nor initiate any exiting (of thread or exe). but... once the thread or exe has exited... the object SHOULD then be deleted. otherwise it hangs around forever consuming memory for no reason other than to store an exit code and any other data the user may have attached to it. thus the object should always be provided to the future (success or failure) because it is basically expected that you "do something about it" and delete the object. if you do not then it'll leak. if you manage to somehow chain things so you delete something else (a parent object etc.) and then this gets magically deleted along with that - great. but most of the time you have to handle the deletion manually because of autodel being bad for c++ etc. > anyway, last but not least `efl_future` (not EINA) is bound to an > object and carries it automatically for you... so basically if the > user needs that behavior, just use that helper. does the promise cancel (and thus call the fail cb) automatically if the obj is deleted? > >> >> > static void > >> >> > _efl_exe_run_cancel(void *data, const Eina_Promise *dead_ptr > >> >> > EINA_UNUSED) { > >> >> > Efl_Exe_Run_Data *d = data; > >> >> > > >> >> > efl_task_end(d->obj); > >> >> >} > >> >> > > >> >> > Efl_Exe_Run_Data *d; > >> >> > Eina_Promise *p; > >> >> > > >> >> > d = calloc(1, sizeof(Efl_Exe_Run_Data)); > >> >> > EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL); > >> >> > d->obj = obj; > >> >> >p = eina_promise_new(sched, _efl_exe_run_cancel, d); > >> >> > EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL); > >> >> > d->promise = p; > >> >> >d->run_future = efl_future_Eina_FutureXXX_then(obj, > >> >> >eina_future_new(p)); return d->run_future; > >> >> > >> >> You do not need to keep the future at all in your structure. You are > >> >> good to go with just : > >> >> > >> >> return efl_future_Eina_FutureXXX_then(obj, eina_future_new(p)); > >> > > >> > then how do i trigger success or failure of the future if i don't have a > >> > handle on it - well promise/future... whatever. same thing to me. i > >> > dislike the whole division of promise vs future. to me it's an async > >> > task that at some point in the future triggers a success or failure then > >> > disappears after that. > >> > >> you just keep the write/send side: Eina_Promise: p... that's where you > >> send your results. > > > > well i need to keep a handle to something. that's my point. > > for this particular case you shouldn't need "d". call > eina_promise_new() without any data OR with the handle used to > *cancel* the promise, that's it and only it :-) i need a handle so i can fulfil the promise later (or cancel on object deletion and thus fail). without that i can't pass on the result. > also, don't keep the reference to d->run_future... it's wrong, you're > not supposed to ever touch it anymore, the ownership is passed to the > caller... > > it's being misused... the one that creates the promise, only keep the > promise... you don't even need to store it in pd or obj in any way, > since efl_future (not EINA) will bind it to the object, whenever the > object dies the promise will be automatically cancelled. ok. that certainly wasn't clear. but i still need something so when the exit happens i trigger the promise success or failure (exit code 0 == success, anything else, failure) > all you need is to pass "p" (the promise!) to the task resolution... > once it's resolved/done, call eina_promise_resolve(). yes - so i need to keep that around. either way i need to keep something. that is my point. > if you have the full code for what you're doing, including when "it > should resolve", I can pinpoint how to do it. Maybe send a Phab ticket > and mark me. https://git.enlightenment.org/core/efl.git/tree/src/lib/ecore/efl_exe.c#n188 https://git.enlightenment.org/core/efl.git/tree/src/lib/ecore/efl_thread.c#n318 those are an eventual result of the run() method. > >> >> > i got to 22 lines and i wasn't even done yet (need to do some more > >> >> > housekeeping)... vs 1 line for event_callback_call. i'm going with > >> >> > events until futures/promises are not a crazy amount of code compared > >> >> > to events. this is it with events: > >> >> > > >> >> > efl_event_callback_call(obj, EFL_TASK_EVENT_EXIT, NULL); > >> >> > >> >> This is absolutely not doing what the future code is doing. You are not > >> >> detecting when a user has removed the handler and so call efl_task_end > >> >> accordingly. You are also not sending a structure with the exit code > >> >> either > >> > > >> > i don't need to send the data in a future - it's stored on the object > >> > anyway. you pick it up with a get from the object. > >> > >> well, that's misuse of the promise result... if you're running > >> something to get data, that's what you should get at the end, not the > >> object... > > > > the object represents the execution of that task (exe) d it survives the > > death of the exe or thread until its voluntarily deleted so data can be > > retrieved from it "at leisure". i don't think storing this data on the > > object is wrong at all because it does need to exist somewhere and storing > > it here is far more convenient. it's not transient data. it doesn't keep > > changing like i/o. it's stored once and stays. > > I think you still don't get it, what's promises meant. i do. see above at the top of my replies. > When someone say "promise you'll run this exe and return me its > result". The promise is exactly that: run until it finishes, get me > its exit code. > > In most systems, Promises are not cancellable. However in EFL we opted > to introduce the cancel behavior in order to be friendly with memory > handling (no garbage collectors and the likes) and resources (ie: why > keep working if that's to be ignored?). > > Then eina_future_cancel(future) will cancel the whole chain. One can > use this, for instance, in a Race, where the winner continues and the > other promises are cancelled. Example: race a process execution > against a timeout, to automatically stop the process if the timeout > happens. > > And note I mention the "chain", so you could have this written as: > > - race( > - exe -> to_string -> text_set -> enable_button > - timeout > ) > > The exe would be executed, the exit code would be converted to a > string, displayed to a label and then a button would be enabled (ie: > "next"). > > If the timeout happens, none of those would happen. > > That's the thing with chaining... while "to_string" already exists, > the others can be easily made into elm and code is simpler to write... > like lego blocks. > > > >> if the user needs the object, he can pass that as his future cb data. > > > > i disagree with this. the data should be THEIR data, not the object. since > > the object has to be deleted on exit or some point after that, the object > > is needed pretty much, so why use up the only data ptr just for that when > > its a necessity? > > depending on how you do it, you don't need the object... see examples > above, the idea with chaining is that you can add more stuff easily. > So take my pipeline and add one more: > > - exe -> to_string -> text_set -> enable_button -> unref(obj) > > the only place you need "obj" is at the end, *none* of the other > handlers would get it... and actually if it was passed, it would be > lost in the first conversion "to_string". the problem is they this is only if they manage to track that exe obj explicitly and track it down at the end of a chain of stuff... invariably 99% of the time i'd say they will want to clean it up right away when they get their exit code/result and not have to figure out how to pass it down themselves. > > no. we can't auto-del the object no matter how much i'd love to. felipe > > would kill me. :) ... > > i guess we're talking about different things here. > > > >> the idea is that these things can be chained, including transformations... > >> like: > >> > >> bla.run().then(uppercase).then(console.log) > >> > >> we even offer some converters and a console future for such things. > >> Actually we should even do more, > >> like those exposed by http://reactivex.io, handling multiple wait, > >> debounce, etc. > > > > i know what they are for. :) > > yes, so take that as inspiration when adding promises: what's the data > that should be passed. Don't see promises simply as "single call > events", that's wrong. they are actually both. they are single call events that follow a standard api etc. as above at the top. > the idea with future is the chain-ability... if you ignore that, it's > almost useless as it's a sub-case of "events". and that's a nice thing about them - they standardize chaining etc. thus making for nice syntax sugar etc. i know this. they are painful to DRIVE from code in C in objects which are running the show in terms of doing the i/o and finally feeding results to the promises. > >> > oooh... no no no. first - i don;t need know now if the handler has been > >> > added or removed because callback_call handles that for me. > >> > and calling end() sent a > >> > signal to the child process to request it to end... it does not mean it > >> > has ended yet. so you would never sensibly call end from the exit event > >> > cb because that's sending end to a process that no longer exists... just > >> > the object representing it does still to store the results. > >> > > >> > so maybe you mean "del()" the object if the caller isn't listening for > >> > exit events... It'd LOVE to do that, but c++ people will hate autodel... > >> > which is exactly what that would be. > >> > >> well, that's exactly what the promise does, and to consider "match > >> behavior", that's what you should do. > > > > the promise is deleted.. the exe etc. object cant be. and that object has to > > stay around because you need long term control like sending term/kill > > signals or handling i/o to and from it. > > you're not deleting the object. you're cancelling the task, no > refcnt(exe) is changed. the caller has to delete (or unref etc.) the object. see above. if they don't it'll stay around forever. > cancelling the task = sending term/kill signals or i/o commands. which may eventually result in an exit from the process which then will result in the current exit event (or the promise success/cancel callbacks if those were used). > >> usually with regular events we'd just "forget" about it and leave the > >> task running, doing its process without cancelling... which is bad > >> most of the time. > > > > yes. that's why the object stays - it's not just a simple promise that > > fails or succeeds. there is an exit code, there is i/o etc. ... > > > > in theory if you don't care about the results you could delete the exe or > > even thread obj and thus lose your connection to it and control lines... > > but then never get a result. :) > > ok, to allow "dangling" promise you'd need to make it unbounded to the > object... so you delete the object and the promise would still remain > alive, not "auto kill" the task. that's just not sane to do (as above - object needed to drive the i/o and listening of the exit of a thread or exe). so deletion probably has to result in the promise failing. yes in theory i could separate the tracking out of the object and have the object just be a "view" into an internal tracker. that'd add a lot of extra code and complexity i'm not sure is a good thing. > While I see some usage for this behavior, most of the time this is > going to lead to incorrect behavior. > > For the rare cases where this could be used, task could use the same > approach as I did for io.closer and define "auto stop on delete" > property. If true (default), once the object is deleted or the promise > is canceled, the task is stopped. If false, it's left running > dangling... with associated handlers (task/promise) freed. the problem with the former (stopping the task is a need to wait for a response to know what kind of exit happened. art this stage i dislike this. i did consider having it block and wait, but decided against it. you can even see commented out code in efl_thread.c to do just this. this is the kind of thing i wanted to have discussions on in that thread/exe etc. thread. not superficial stuff like args - i added the void *'s easily enough but everyone id distracted by things like that and not more core questions like this. should delete stop/kill a task ... and should it then wait for a response? the latter is the cleanest, but its is fragile if threads or exe's refuse to nicely exit. > >> >> (and of course you are not describing it in the .eo file). I don't even > >> >> see how you can compare this two lines ? And we are not even looking at > >> >> the user of the API here which is what matter even more. How do you > >> >> make sure that the event is always delivered properly ? How often do > >> >> you generate the event (Every time someone register a callback) ? There > >> >> is a lot of open question with this kind of API that you are just > >> >> disregarding here. > >> > > >> > it's called when the parent process knows the child has exited and what > >> > the exit code is. there is no guarantee this happens during the life of > >> > the object at all. the process could become hung on a kernel syscall and > >> > never exit, ever. that's what the exit event is. when this happens. it's > >> > guaranteed to be called when this event occurs. > >> > > >> > my point is the event is 1 line. futures is a massive blob of code to > >> > achieve the same goal. i find them basically unusable to the point that > >> > i'm just avoiding them now. if they were "1 liners" too... then great. > >> > but they are not. (well they will be a line to create and store, a line > >> > in a destructor to cancel on destruction if still there and another line > >> > in the "where the event happens" to either call success or failure with > >> > the future data... but it needs to be far far far simpler than it is. > >> > >> you just realize that the code is always the same, right? there is no > >> magic, we're just doing something on behalf of users, in a clear and > >> standard way. > > > > ummm it's not the same. its far longer. even if its half of what i described > > it's still far longer than an event. > > I'll not even bother to explain it's doing more on user's behalf... I > tried, Cedric tried, seems you don't want to see. i know that.. i'm not talking about the outside api to users. i'm talking about the inside api to efl to drive them. > but part of the extra work is due your misusage of the api. other storing run_future too.. i still need to store SOME handle to this future/promise thing so i can trigger it with "it's done - here is the result". > >> so yes, you're doing bit more work so users don't have to. Like > >> keeping references, canceling, etc. Our hope is that core devs (that > >> usually deal with eina_promise part) will be more careful with > >> managements and the likes. > > > > a LOT more work. and the user doesn't do less.they still have to provide a > > function to call on exit. > > what? the user of the api still provide a function to be called. in fact has to provide 2 (success and failure). > >> users just pass one callback and *always* get called (even with > >> "ECANCELED"). > >> > >> as for "lines of code", for sure we could offer a way to "add a > >> promise to this object", it would fetch the "loop" from a loop-user, > >> then fetch the scheduler... then create the promise and return. > > > > that's my point. that isn't there. and promises/futures code is just a > > hairball that figuring out how to add it is not something i want to spend > > time on. i've already fixed some things there, but i have gotten to the > > point where i now am going to work around them instead. i am not even using > > the timeout futures anymore as they crash (i decided to set null to the > > content to try and catch it more easily rather than have junk)... > > > > they are just not worth the pain IMHO as it stands. > > as I commented in the other PR, it's a bug somewhere else, we couldn't > see the failing code, but even with that the promise should be > protected by mempool checks, if it's crashing at least the mempool > check is buggy (since it should say the memory isn't valid). the failing code happens in non-main loop loops it seems. :) i had it crash every single time. i gave up and used efl loop timer objects instead in my examples on the phab wiki page on exe/thread/task .... try replace them with timeout futures and see. :) > >> the "cancel the promise" part is already done by the efl_future (note: > >> "EFL", not "EINA"), which binds the future/promise to the object > >> life... on destruction they are automatically cleared. As Cedric said, > >> once old efl_future dies and Eolian gets future<x> mapping, this will > >> be generated for you. > > > > it's not there. it's not documented that well and certainly doesn't have the > > helpers needed like above, and minimal good examples that are concise and > > simple. > > ok, examples and docs may be missing indeed. so i go by what samples i can find and i base my code off them... and then as i trace them more and i have 10 then 20 lines of code i stop and go "wtf?". > > so either make futures sane and sensible to use from within efl, or give > > up. i certainly am not writing a huge blob of code every time a future > > needs to be used. efl wax created originally to serve enlightenment and > > it's needs. e is in c. associated needs are too. futures do not make things > > simpler or easier there. it's some nicer syntax sugar in js etc. ... but > > this is just making code harder to use and worse for ourselves in the name > > of a currently non-existent js usersbase. if it came with "minimal effort > > beyond events" then fine. support this theoretical userbase and maybe it > > might happen. it's not minimal. that is my point. i don't see it moving > > anywhere either at this point. :( > > time to step back and try to understand what's a promise, a future, > chaining and how to properly create and use a promise. > > docs may be missing, indeed... I can't help with that right now (lack > of time), but for sure the concepts/theory part is broken in your > head... sorry :-( i know full well what they are. what i am pointing out is they are a pain to drive from the efl side. and there are bugs lurking in them to boot. hard to build anything on top. > -- > 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 > -- ------------- Codito, ergo sum - "I code, therefore I am" -------------- Carsten Haitzler - ras...@rasterman.com ------------------------------------------------------------------------------ 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