Hello,

Ok, I am giving up on this. We will make it an eo object, but not an
eolian one as it should be a native type for any binding (It is sure
that C++, Lua and JS will have to do a manual binding for it) as there
is very little case were inheritance make sense on promise and
wouldn't at the same time break binding. eina_promise_owner will
become eo_promise and eina_promise will become eo_future (to follow
C++ naming convention). Not to sure how to limit the interface exposed
by a return type as we don't want the user of the API to access the
promise API, just the future one. Double object would be annoying, but
is likely the only solution. So if we want to be safe, we will need to
use 2 objects per promise. One facing the user of the API and one used
by the producer of the API. If we don't want to be safe, we can merge
both API and just use inheritance. So returning an eo_future interface
in the API, while in fact eo_promise function would work on it.

On Mon, Jun 13, 2016 at 7:23 PM, Jean-Philippe André <j...@videolan.org> wrote:
> On 14 June 2016 at 02:28, Felipe Magno de Almeida <
> felipe.m.alme...@gmail.com> wrote:
>> Sorry for top-posting. But let me summarize the Promise lifetime:
>>
>> A Promise when created starts with a ref-count of 1.
>> If the promise is not needed anymore, it should be
>> unref'ed (if no eina_promise_then is made, either
>> directly or by eina_promise_all/eina_promise_race).
>>
>> The first eina_promise_then _steals_ the first
>> reference, the others eina_promise_then
>> increments the reference count.
>>
>> When the promise is fulfilled (by value_set or error_set),
>> then each call to a eina_promise_then decrements
>> the reference count by 1.
>>
>> When the reference count reaches 0 _and_ the
>> promise is fulfilled the promise is deleted (both
>> conditions must be true, if just one is true it
>> continues to exist).
>>
>> So, valid cases:
>>
>> Eina_Promise* p = ..;
>> eina_promise_then(p, ...);
>> // promise will be deleted
>> // when callback for the then is called
>>
>> Eina_Promise* p = ...;
>> eina_promise_unref(p);
>> // Promise will be freed when promise is fulfilled
>> // by error_set or value_set
>>
>> Eina_Promise* p = ...;
>> eina_promise_then(p, ...);
>> eina_promise_then(p, ...);
>> // promise will be deleted
>> // when callback for the then is called
>>
>> Eina_Promise* p = ...;
>> eina_promise_all(p, ...);
>> eina_promise_then(p, ...);
>> // promise will be deleted
>> // when callback for the then is called
>>
>> Eina_Promise* p = ...;
>> eina_promise_all(p, ...);
>> // promise will be deleted
>> // when callback for the then is called
>
> So my understanding here is that this can work because a promise can not
> ever be synchronous.
> In other words, eina_promise_then can never trigger the success/error
> callback to be called synchronously, otherwise the above reference count
> system falls apart.
>
> While this approach is convenient, it means that we can use an object
> (promise) we don't hold any reference on (after first _then).
> This is weird :)

Agreed.

>> Etc..
>>
>> On Mon, Jun 13, 2016 at 2:17 PM, Cedric BAIL <cedric.b...@free.fr> wrote:
>> > Hello,
>> >
>> > On Sun, Jun 12, 2016 at 10:13 PM, Jean-Philippe André <j...@videolan.org>
>> wrote:
>> >> On 13 June 2016 at 03:02, Cedric BAIL <cedric.b...@free.fr> wrote:
>> >>> On Wed, Jun 8, 2016 at 4:16 AM, Carsten Haitzler <ras...@rasterman.com
>> >
>> >>> wrote:
>> >>> > On Mon, 6 Jun 2016 06:01:13 +0200 Cedric BAIL <cedric.b...@free.fr>
>> >>> said:

<snip>

>>> Events are the same: they call a callback function with a user data and
>> an
>> >> event info (promise value).
>> >
>> > No. Event in promise will keep their value until all the reference to
>> > the promise exist and each time that a callback is triggered that
>> > reference count goes down by one. There is also an absolute guaranty
>> > that you can not destroy a promise without having all the reference
>> > receiving either a succeed or a fail. Also the returned value of an
>> > event handler for then or cancel should be ignored as you can not stop
>> > notifying anyone in the chain and the state should stay either success
>> > or failure once it has been decided.
>>
>
> It is actually a good question whether the return bool is a good idea or
> not.
> If you return false in evas callbacks, you're gonna be in trouble.
>
> The ON_HOLD flag, now called efl_event_processed_get/set() is a better
> approach to stop processing events.

That is off topic, but seriously something we should consider asap if
we want to drop the return type of event. I have not any case in mind
where returning EINA_FALSE make sense. Should we drop it ?

<snip>

>> >> Anyway it seems the only difference here is that a promise starts the
>> >> action as soon as possible, while an eo object would have to
>> explicitely be
>> >> marked as ready (which is very similar to starting the action during
>> >> eina_promise_then).
>> >
>> > No, the main difference is that the life cycle is linked to the
>> > callbacks and that their should not be any way to bypass it. eo_del
>> > should also be forbidden for example on a promise as only a cancel
>> > make sense. Of course we could alias it, and make sure that cancel
>> > don't destroy the parent. We can also override all eo event API and
>> > make sure they do what we want, but we can't at the moment override
>> > eo_ref and eo_unref. The alternative would be to create an Eo_Promise
>> > which doesn't inherit from Eo_Base (Or make an Eo_Light that both
>> > would inherit from) and would kind of make clear that it is not an Eo
>> > object, but a promise object (Given that eo_ref and eo_unref become
>> > virtual function).
>> >
>> > Also at which point do you think user are going to be confused by an
>> > Eo object where every single function call on it has its own
>> > documentation and doesn't behave like a normal eo object ?
>
> That's where we still disagree :)
>
> I still have trouble understanding how fundamentally different those two
> objects are.
> In fact I see a lot of similarities between a (conceptual) promise and
> Efl.Part.
>
> From my understanding, the main difference is that a promise callback WILL
> be called, be it the error or success, one of them will be, for each _then,
> _race and _all that was setup. Which can be enforced with a commit approach:
>
> // same syntax as eina_promise:
> p = promise_function(obj)
> eo_promise_then(p, _success, _error, data) // this is a HELPER based on eo
> events, implemented in Eo.Promise, steals the ref
>
>
> // it would be equivalent to:
> p = promise_function(obj)
> eo_event_callback_add(p, PROMISE_EVENT_FAILURE, _error, data)
> eo_event_callback_add(p, PROMISE_EVENT_SUCCESS, _success, data)
> eo_promise_commit(p) // steals the ref
>
>
> // or:
> p = promise_function(obj)
> eo_event_callback_add(p, PROGRESS_EVENT, _progress, data)
> eo_event_callback_add(p, PROMISE_EVENT_SUCCESS, _success, data)
> eo_event_callback_add(p, PROMISE_EVENT_SUCCESS, _success_watcher, data)
> eo_event_callback_add(p, PROMISE_CANCEL_SUCCESS, _canceled, data)
> eo_wref_add(p, &p2)
> eo_data_key_set(p, "my data", stuff_that_i_need)
> eo_promise_commit(p)
>
> etc...

commit doesn't work well with bindings and library that will not know
when to commit a promise that is given to them. Basically only case
where commit is to be called is in the application itself and never in
any API that expose one. I don't think this is a solution.

Only viable solution if to be done with eo is if eo_ref and eo_unref
either send a message or can be overriden. If so, we can implement the
following :

p = promise_function(obj);
eo_promise_then(p, &_then, &_cancel, &_progress, NULL);

and

p = promise_function(obj);
eo_ref(p);
eo_promise_then(p, &_then, &_cancel, &progress, NULL);
all = eo_promise_all(p, p1, p2, p3);
race = eo_promise_race(p, p2, p2, p3);
eo_unref(p);

We would silently eo_ref in eo_promise_then and eo_unref would start
acting if the number of ref == number of function. Once it start
calling each function, it would eo_unref and the last call would drop
all reference to eo, destroying the object.

> _race and _all could be implemented in Eo.Promise, and would make the _all
> promise get an extra ref on the sub promise.

As _race and _all are most likely already existing as a native object
in all bindings, it will be only useful for C. Implementing them on
top of an Eo.Promise wouldn't be a real problem and we can make them
more convenient as we do not need to follow eolian constraint there.

> And then the eo event callback data is "data", the event->info is the value
> (void* anyway).

Yes.

> Internally, all we need to make sure is that all the callbacks will be
> called (error or success), and the promise object can't be deleted until
> then.

We need to also override eo_del to actually act like a promise_cancel.

> Also need to ensure that we unref() the promise object after it called its
> callback (this can be done in each promise implementation - no infra).
>
> What happens if cancel() is called during a success callback? Will the
> cancel or error cbs be called? Anyway this is a tricky situation, similar
> to eo_del() during a callback.

This is forbidden. The state of a promise is defined once and for all
by the first value set or cancel. eo_del will have a warning and do
nothing on a promise which as already succeeded, but hasn't finished
yet for whatever reason. If it doesn't follow that rule, it breaks
assumption on promise making them pretty much useless.

> Now, if you forget about Eo, Eina, ... and go back to C++, you will agree
> that anything is an object. A promise would also be an object, an instance
> of a class. Why would it be any different with our object model?

Promise would be a template provided as a native type in C++ and be
named future. C++ object model let you implement a lot of different
life cycle management, funny GC for example, without enforcing
anything. There is no such thing as eo_ref/eo_unref on a C++ object.
This is something you implement yourself on top of C++ object model.
Same goes with weak ref or smart pointer. They are something you
implement on top of the language. This is not the case of what eo
does. Eo come with its own life cycle management, and we currently
can't implement promise without change to eo and overridding a few
base function (ref/unref and del for sure, not sure for weak ref and
friends).

Every language now provide this as a native type.

> So, yeah, still not convinced we need a different event signature, a
> different ref/unref system (where, as seen above, we can manipulate objects
> of which we don't own any ref), and lose on the way all the fancy features
> that eo provides.

With change to eo, it is doable. Still think we are forcing a cube
into a smaller cilinder, but well, I have some good hammer, so
whatever !
-- 
Cedric BAIL

------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity planning
reports. http://pubads.g.doubleclick.net/gampad/clk?id=1444514421&iu=/41014381
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to