On Sun, 11 Mar 2018 16:49:37 -0300 Gustavo Sverzut Barbieri
<barbi...@gmail.com> said:

> On Sat, Mar 10, 2018 at 6:42 AM, Carsten Haitzler <ras...@rasterman.com>
> wrote:
> > 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.
> 
> not really, value is core part of it :-)

i guess we disagree, but the call without the call to the success or failure...
value is pointless as you don't know what it is, so the calls come first in
importance, value second.

> >> 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. ...
> 
> not sure if you checked the efl_future (EFL, not EINA) version I told
> you... the object goes there for you... no need for you, the promise
> creator, to do that...
> 
> the promise create should only care about resolving a value or rejecting it.

in this case it'd be resolving the result of an action of an object - same as
file_set. the object is the key thing here, not the value.

> > 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).
> 
> this breaks promises completely. You need to pick a concept and stick
> to it. For example, if we're allowing this. "detach" thing, then add a
> new ERROR_DETACHED or the likes and use that in every promise that may
> be disconnected.

it's not that practical to keep the promise going after the object is gone.
same for file_set on an object. why does it or should it continue after the
object the action is fore has gone away? it should just fail at this point as
there is no point delivering the result of a file_set on an image object if the
object has gone - same as the exit result of an exe obj if the exe obj doing
the tracking of the child has gone.

> However, as I wrote before, more than just doing that, allowing the
> user to control the behavior would be nice, given the very rare cases
> detach is expected could do 1 prop-set more to get desired results.

right now "you just aren't allowed to do this unless you no longer care about
the exe". deletion == you don't care anymore.

> > 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.
> 
> here you're mixing things. It traces back to the other discussion
> about invalidate x reference... the object should be invalidated,
> sure... not deleted as refcounts shouldn't be messed up.

i can't control if someone dels or unrefs the object to 0. i can only control
what is done about it. i'm talking about that case where the object will be
destroyed etc.

> then check that discussion on "release ref on invalidate"
> 
> it's not particular of promise/future.
> 
> 
> > 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.
> 
> that's because you're solving the wrong problem at the wrong layer.
> See above, mixing invalidate x ref...
> 
> last but not least, if you read my examples above, a single future can
> solve this in every case:

what examples above? i see this below...

>     efl_ref(obj)
>     eina_future_chain(obj_returns_a_future(obj), ...
>         efl_future_unref_cb(obj)); // this returns Eina_Future_Desc
> that efl_unref() obj once the future is resolved, can be the own obj
> or another
> 
> Check how promises are used elsewhere, they are not "one big single
> callback", rather small pieces that are chained together. It's not
> "single-shot-event", as you're trying to do :-(

you're talking about how someone USES the promise callbacks etc. ... and i am
only talking about how you handle the other side of them - the code that has to
create a future/promise, track it, resolve it etc. etc. ... 

> it would help if you do give it a try as an user... otherwise you're
> creating an API intended for users you really don't know.

i have used promises as a user. i do not find them better than events. read the
code i wrote even to see. git log -U check my commits and look for future or
promise there.

> >> 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?
> 
> YES, that's what efl_future does... it binds the future to the object
> life... it will be the default for .eo, as eolian will generate these
> calls for you (at wrapper layer, so you don't need to... that's why
> it's marked in my code, so we can remove the manual calls). Some API
> will be able to return unbounded future using some special attribute.

so that's 1 line i don't need in the destructor then. good.

> >> >> >> > 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.
> 
> in your task case, since you can't run more than once, this can go
> into the pd->promise...

correct. so my point still stands - i do need to track this extra handle.
that's 1 more struct member, 1 line to NULL it after it's resolved just to clean
up (though not needed strictly as i've forbidden multiple runs - a 2nd run
will fail now).

but ok - this extra code is kind of expected as i have to rack a specific
"thing".

> >> 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)
> 
> note that this needs to be a convention. For instance, most HTTP
> frameworks do NOT reject the promise on HTTP status not in
> 200-range... their convention is that it will return the promise with
> the status. Rejection just occurs on network failures (ie: the HTTP
> request didn't finish).

at least for the case where the obj is deleted (goes to 0 references.
destructor called) i don't think keeping the promise running is sensible (for
now - it can be de-limited later if enough code is written to split the obj
from the tracking).

> If you ask me, for processes, rejecting on status != 0 is bad since it
> will be harder to pass the value (there is no standard for you to
> convert that to an Eina_Error, or "Exception" or "Error" in other
> languages). And some people do use return status as "0" (all right)
> but others to indicate the kind of failure (ie: 1 = invalid params, 2
> = failed resources, ...). These won't be able to use your promise.

but != 0 is that a process failed. "file not found" or "io error during cp"
etc. etc. - the shell considers it a failure when you do

cmd1 && cmd2 && cmd3

which is analogous to

cmd1.run().then(cmd2.run().then(cmd3.run()))

cmd2 will never run if cmd1 returns non-0... 

indeed the return value if not 0 indicates the kind of error. there isn't a
standard to convert that indeed other than "that's an error" which is about all
I can do. say it's an error and offer the code.

> that's the reason why the http/promise people use that, so 50x is
> handled on way, 40x is another, etc. very similar proble.

the problem with exe's is non-zero could be "command not found" too. it's an
error tat tells you the desired result of the execution failed somehow... it's
pretty coarse. :(

> >> 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.
> 
> you'd need that anyway since you can't allow more than one run to exist...

yes. that was my point - i have to keep the handle around so i can fulfil it
later. :) you were telling me i don't need to do this. :)

> >> 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
> 
> 1) if you're using promise, why are you emitting an event? Remove that

i'm NOT using a promise. that was the original email. i started writing the
code tyo do the promise got to about 20 lines and went "WTH? this is insane"
and just made it an event. i quote the original:

"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);"

:)

> 2) there is no need to keep exit_called
> 3) there is no need to efl_ref

since it's a job - there is. the exe obj may be deleted and thus the job called
with no exe obj around. well.. there is nothing that tells me if the future
will or will not be magically cancelled if obj (the data ptr) is deleted. as
its a generic void * i'm not making that assumption.

> 4) there is no need to efl_job, promises are already resolved in the
> next mainloop iteration (clean context)

they don't resolve in-line there and then like callback_call() ?

> your _exit_eval should be:
> 
> if (pd->promise) {
>    eina_promise_resolve(pd->promise, eina_value_int_init(status));
>    pd->promise = NULL;
> }
> 
> that's all. Also it seems you're trying to call exit_eval too early,
> ie from broken pipe... shouldn't you let the process exit, be
> collected by waitpid() and JUST THEN you call the promise resolve from
> a single place?

aaaah it's a race condition. the eval checks that all stdout has finished being
read from the process and that now errors on reading AND has to check the
exit code has come in. when BOTH of these have happened, then you get the exit.

the user of the api doesn't need to worry about this then as the race is hidden
and handled and everything serialized.
 
> > https://git.enlightenment.org/core/efl.git/tree/src/lib/ecore/efl_thread.c#n318
> >
> > those are an eventual result of the run() method.
> 
> like above, code is very similar.

it's practically identical. :)

> >> >> >> > 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.
> 
> reading what you wrote and the code you pointed me, you don't...
> 
> it would help immensely if you give it a try for real... before you
> implement the promise side (provider), give it a try as an user... get
> some js stuff, they are full of promise, then you use to implement
> something, then you come back with your experience...
> 
> ie: https://github.com/axios/axios (http) + https://redux-saga.js.org
> (state management) ?

i've seen the code before. well code full of then's etc. - i know the syntax
sugar. i'm not talking about the "consumer" side. i'm talking about the
"producer" side inside efl.

in C the code difference for the vast majority of cases is the same - you have
to provide a callback. in the past i have rarely chained events in C. in edje -
sometimes. maybe 10-15% of events chain multiple anims. in C ... hardly ever.
that's my point. it has some syntax value for SOME use cases. they are not the
majority in my experience. if i had to chain things, i did. i didn't avoid it
"because it was hard". it wasn't. in the callback just have 1 line to do the
next step

> >> 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.
> 
> in such case, just invert the order, that's the beauty of it:
> 
>  exe -> unref(obj) -> to_string -> text_set -> enable_button

but they need the obj handle. i dislike making them pass the obj handle as a
data ptr when it's almost always going to have to be used. imagine the pain of
evas events or eo events if you didn't have the obj already passed in and you
had to use the data ptr for that? i can imagine the pain... it would be rather
unpleasant. in this case the thing we are primarily talking about is the exe
object, not the exit value. the object imho is the value. it's a container
value.

> this still works, since to_string operates on the result (int), there
> is no need to query the value from obj (exe). But say you needed, you
> can still reorder:
> 
>  exe -> to_string -> unref(obj) -> text_set -> enable_button
> 
> etc
> 
> 
> >> > 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.
> 
> then you miss a big part of its usefulness :-(

i don't see how i do...  a standard api leads to standard syntax like if (x)
{a} else {b} but for events over time rather than  procedural logic flow. that
makes it easier in languages like js where the syntax does come out like this,
IF you need this.

in C it doesn't come out that nice in syntax vs events realistically. they are
much the same.

they are still single call events with success or failure. when CHAINED you can
do the over-time async equivalent of

if (a) {
  if (b) {
    if (c) {
      party();
    } else {
      cry();
    }
    passout();
  }
  leave();
}

that's a logical conclusion, but it doesn't change what promises/futures are.
they are one async if (a) {} else {}.

> >> 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.
> 
> Well, in the code I read they were painful because you overdid things.
> The flag isn't needed, the job isn't needed, the ref isn't needed...
> then of course, it's start to be cumbersome! :-D

what happens if the exe obj is deleted then while the job future is still
going? (for the ref). the exited flag i guess is superfluous as
pd->fd.exited_read being -1 says the same thing, though this has nothing to do
with promises or not. it's part of the race fixup above between exit signal and
the stdout from the child being all read and flushed.

> if you ask me, the real PITA with promises in C is lack of lambda.
> This is the real pain, having to declare the callbacks OUTSIDE of your
> context, not being able to capture...

that's actually why i think syntax-wise they don't come out ahead in C.

> I had proposed to implement c++ lambdas in C as a pre-processor... but
> nobody wanted to pay.

there already is a pre-processor that does this. it's just a matter of using
it. lambdapp instead of gcc or clang or cc ... :) the problem is more that some
people have major objections to having a preprocessor. "it's not C anymore if
you need a different compile command than your normal c compiler" is the
summary.

i kind of don't agree - i think it's just augmenting the normal C pre-processor
with a more intelligent one in front of it. there's a lot of "boilerplate
footwork" that a smarter preprocessor could cut out. like lambdas. :) i agree
with you on this. others don't though.

> nonetheless, if we start to use promises for real and correctly, we
> can start to add some "future cb" library to make that less painful.
> We did some examples, like type-convert and "print to console"... but
> linking to UI would be super-nice (ie: enable/disable, text-set,
> focus, ...). Also some flow control (ie: call this parallel chain if
> this condition is true, ie "value == 0") and merging multiple chains
> (ie: wait button click AND data AND timeout... then emit a tuple).

that is where standardised if/else nature of promises does come in handy - you
can build stuff on top rather generically.

but my issue is not the outside use of them. i'm neither here nor there on the
consumer side. its the producer side that is more painful.

> and that's why I'm insisting so much on the value thing... if you do
> have a proper value for exe, then you can get this. With that you can
> even do some UI editor or save this as as part fo eet (ie: Gstreamer
> offers "gst-launch" to test stuff, can load pipelines from xml...)

since the actual value of return is don't generically interpretable except 0 ==
success, anything else == failure... i don't see a lot of value in the "exit
code" value. the object is of far more use than the exit code.

> >> >> > 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.
> 
> this is the invalidate x unref discussion from the other thread.

actually no. it's just "has to go to 0 refcount so destructors are called". if
thats via a del or unref is not relevant here. :) it has to get there one way
or another or things leak.

> anyway, you can explicitly add the future to do that OUTSIDE, which
> would enable things like this:
> 
>   eina_future_chain(some_future_creator(x),
>       efl_future_cb_unref(obj1),
>       efl_future_cb_unref(obj2),
>       efl_future_cb_unref(obj3)
> );
> 
> this would unref all 3 objects once the future dispatches... note
> efl_future_cb_unref() doesn't exist, it need to be created like the
> other helpers that returns Eina_Future_Desc...

then you can only pass in obj1/2/3 via data ptr and then you cant use the data
ptr for "your own data". :(

> >> 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).
> 
> and isn't it the desired? Just note that since the promise is already
> rejected with ECANCELLED, it won't be rejected anymore...

i'm not sure sending an int signal == cancel. it does say stop to the process.
interrupt. but ... it may not mean the process failed. it may have done its
job and was waiting around for more messages...

i think the success or failure of the promise in this case would be based on
the exit code alone.

> >> >> 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.
> 
> here I'm talking about forcing dangling threads or processes... there
> are cases for those, for them making a property... however they are
> not the common cases, not even sure we should care to support these
> anyway

i think we agree on this. for now i don't want to support this as it's a fair
bit fo extra work. but if someone does delete the object before the task has
finished ... it needs to be dealt with sensibly. to me than means at that point
cancelling the promise that's pending.

> > 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.
> 
> not sure you got the idea, but if you cancel a promise/future-chain
> you get a resolution ECANCELLED automatically... there is no reason
> you should wait it synchronously to finish, basically:

no no. the stop and wait has nothing to do with promises. it has to do with
policy - do we do this or not to get the result of the task?

> exe_promise_cancel(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
> {
>    Exe_Private_Data *pd = data;
>    kill(pd->pid, SIGTERM);
>    pd->promise = NULL;
> }
> 
> that's all... the ECANCELLED is used automatically to reject, all you
> need to do is to invalidate the promise pointer (thus it's given as
> parameter, if you have more than one that could be the pointer to make
> NULL, like in a list/array).

i think we're talking about different things. i was talking about what to do
when the obj is deleted but the task hasn't exited yet. it's going to happen.
my take is you don't wait for the task to exit. you just detach and then cancel
any pending promise internally. the "user" doesn't do this. this is a
side-effect of deletion of the object. the promise will get cancelled so it has
a resolution of some sort.

> >> >> >> (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.
> 
> let's start with fixing the code? Once it's correct we can think about
> adding some helpers. the one efl_promise_new(), that creates a promise
> using an efl_loop_provider object as argument looks like a good one to
> start with.

so what code is needed instead of calling the exit event and the end of run()
to create the promise? what minimal code is needed?

in run()

  pd->promise = ....?

in _exe_exit_eval()

  ...(pd->promise, ...)?

i'll assume that destruction of the object cancels the promise, so nothing in
destructor needed.

> >> 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".
> 
> but flag isn't needed, job isn't needed, ref isn't needed...

well flag is needed to only have run done once ever. i could have a dangling
promise ptr indicate that too, but there is none right now. i think the ref is
needed (see above), but job may not be needed if what you say is right - that
they are always delayed by a job.

> >> >> 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).
> 
> not really, this is the helper to auto-check for errors... the core is
> only one function and you'd need to check the value (eina_error or
> whatever you need).
> 
> 
> >> >> 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.
> 
> if you try to drive a car like a motorcycle, you're going to have problems
> 
> 
> > and there are bugs lurking in them to boot. hard to build anything on top.
> 
> no doubt can exist bugs, cedric spotted one that is with a
> not-so-common case we're trying to solve (return a resolved promise
> from a future, not common as you'd normally just return the result,
> not a resolved future... but still a bug).
> 
> I can't test your example atm, not even a linux machine/vm hanging
> around and time is short for me... but I hope others can give it a
> try. If the bug is really on the promise/future I'll try to assign
> someone to check it

the bug i spotted was with the timeout futures... (thats why i decided to set
things to null so i could easily know we're accessing freed memory somehow).

i can change my code back into "crash mode", but i was busy working on the
efl.exe/task/thread/appthread/app code and sample code and i just wanted
something that worked for now and didn't want to dig into deeper issues
that prevent it from working.

-- 
------------- 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

Reply via email to