On Thu, Jun 16, 2016 at 12:11 PM, Tom Hacohen <t...@osg.samsung.com> wrote: > On 03/06/16 07:42, Carsten Haitzler wrote: >> ok. interacting with promises... >> >> these are just a mess. >> >> 1. the value thing is just odd. >> 2. they are complex to set up inside our api (setting them up setting cancel >> cb's and more) >> 3. they totally screw with what eo and interfaces was all about - making the >> api EASIER to use. promises make it harder. >> >> why harder? longer lines of code with more parameters and more special >> casing... but the WORST... >> >> void _cb_promise(void *data, void *vaue, Eina_Promise *promise) >> >> that's a promise cb >> >> Eina_Bool _cb_event(void *data, const Eo_Event *event) >> >> and that's an event cb. they are different. eo events were meant to simplify >> and unify our callback handling. to have a single cb signature. now promises >> break that. this is just bad. >> >> i wasn't sold on promises. i was skeptical, but whatever... but now i am >> seeing >> they are visibly making things worse. code is harder to write, harder to >> read, >> harder to maintain, harder to get right. now we have timeouts that cannot >> repeat. no - creating a new timer in the cb is not repeating. it has to >> repeat >> with the "zero time" being the time when the timer was ticked off, not "now". >> >> please - everyone. take a look at promises and how they are used. forget all >> of >> the "but node.js has them" and all the "i can chain promises" and so on. the >> BASIC usage of them is harder now in efl. >> >> what to do? well... minimize their use for one. do not use them unless you >> ABSOLUTELY HAVE TO. also promises should become eo objects with event cb's so >> they work just like everything else. i can ref, unref, delete and whatever >> them >> like everything else. >> >> right now i think promises are just not in a shape to use or ship. they need >> a >> lot more work. i think we need to drop them for efl 1.18 and defer for efl >> 2.0 > > Guys, you bored me to death with this thread. I replied to what looked > like it needed attention, but please let me know if there's anything > specific I missed that warrants my reply. > > I would like to add my take on promises too. As you may remember, I've > objected to promises from the start. I just didn't see a sensible way of > implementing them back then, and unfortunately, even after they've been > implemented, I still don't think they are done nicely.
Being against promise, is only advocating for no asynchrone behavior in Efl. This is a position we can't take. So either you have a better pattern to handle asynchronous behavior and synchronisation or we have to provide promise. Objecting to promise is clearly not helping anything here. <snip> > Lets start with life-cycle: Eo is great, and I think using Eo is the > right way to go, but unfortunately that doesn't solve our life-cycle > issue. When do promises die? > > p = efl_file_set()... > // Delete here if file has been set? > promise_then_add(p, cb) > // Delete here if file has been set? > promise_then_add(p, cb2); > // Delete here if file has been set? > ... // a million years into the future > // Delete here if file has been set? > > There is just no sensible way to do it automatically. You will *always* > have to unref, so following the above example: > > p = efl_file_set() > ... // All of the code before > eo_del(p); // It'll only die here > > Which won't work because then ignoring efl_file_set's output won't be > allowed. > > The only sensible way of doing it, I guess, is to force a wrapper of > some sort, so the code above becomes: > p = eo_ref(efl_file_set()); > ... // Code from before > eo_del(p); Amazing, we are more or less back to my point. Except that eo_del is now to be used. The problem is that this break things, first you loose the parent relationship on promise. So if that was used to actually do something useful on its lifecycle, you can't anymore. Second problem, you have to do eo_del only once you have actually sure you will never cancel that said promise. Otherwise how could you still have a handler on it somewhere ? If user don't get confused by the fact that they can stil do an eo_promise_cancel on a promise that has been eo_del... So if you have to delay eo_del by the time you actually have cancel/then, hum, you kind of ruined the point of having promise as you now need to track actively its live cycle and do a del when the cancel/then you care about happened. Of course this start to be messy as you don't really know how many callbacks are registered on it and things kind of fall apart. If you are willing to accept the confusion that you can call eo_promise_cancel on an eo_del promise, then your proposition is doable. I think this is pretty bad. > Or probably for extra safety (like marking the promise was actually > used, and not just implicitly with a ref, this will allow us to block > then/cancel registration and be more safe). > > p = efl_promise_use(efl_file_set()); > ... // Code from before > eo_del(p); > > efl_promise_use will set a flag in the promise so that > p = efl_file_set(); > promise_then_add(p); // This will fail because the flag wasn't set. That's bad. Being able to have one set of callback set without the need to do a lot of mess is really neat. Don't forget that the only use of promise is in C. In every other language they are going to be manually binded and integrated with the native type. > So that's safe and probably the way to go. This will let us manage > life-cycle correctly. If you really can not provide a signal in eo_ref/eo_unref or a way to override it (Which I think is doable for very little cost by watching callback add/del and in most case just doing a not taken if), then the proper way to do management is to only allow one then/cancel/progress per promise and return a new promise when you set it. This way, there is no problem with refcounting at all as you only have one then. p = efl_file_set(); p1 = efl_promise_then(p, &then, &cancel, ...); // set the callbacks and return a new promise that is chained to p p2 = efl_promise_then(p1, &then1, &cancel1, ...); p3 = eo_promise_all(p2, ...); p4 = efl_promise_then(p3, &then_all, &cancel_all, ...); efl_unref(p4); This could work and would be less confusing than using eo_del to mean something that is not del. Obviously the cost is that there is going to be a lot of eo object created, and performance will sucks, but who cares. Obviously you could do a weak ref to store p and cancel it at any time using that weak ref. This is the only way I can see implementing this without change to eo_ref/eo_unref. Still this is going to be a huge performance problem. It will consume one Eo object per set of callbacks being set ! This is going to be massive in memory allocation and in cycle of allocation/free. We better optimise our allocation speed in Eo and our recycling of memory, if we go this way. I still do think that being able to have a signal on eo_ref/unref would be lighter and better in this case. > Usage of event callbacks: I mentioned it somewhere else in the thread, > but not as bluntly. I think Marcel is wrong, and I think you guys are > focusing too much on non-existent semantics. Saying promises are > callbacks that are only called once so they are inherently different > from event callbacks is absolutely wrong. Think of EO_EVENT_DEL, called > when object is deleted, only called once. You are too fixated with how > events happen to be implemented in eo.base, don't. > As I also said, overriding callback_add and adding there code to call > the callback immediately if the promise has already finished is > *exactly* the way to go. Also, you don't need to remove callbacks once > they have been executed, they just happen to never be called again > because the callback is never triggered again. Implementing the callback by overriding the callback*add/del/call is obviously needed. A pain, but if we do eo, obviously the way to go. > Splitting of promise to two objects, owner and future: unnecessary. > There are a few mechanisms in Eo to let you have different "access" to > an object. > 1. Make all of the owner methods "protected", so assume whoever is > implementing "owner" is more responsible and let him have access to both > the "future" functions and his "own". Protected doesn't work. Thinking there is a responsible side and a less so, is not proper type checking. It will also make it painful to create a new promise for anyone. protected has to be used in a very limited set of case and particularly not when it is an API that is supposed to be used by almost everyone. > 2. Same assumption as #1, but just make Owner inherit from Future, and > create Owner internally, but return Future in the API. Won't have a > different in C, but for bindings it'll only expose the correct type. As said before, we do not care about bindings at all. They will do it manually in absolutely every possible case. The only important part of this API is the C side. As for inheritance, yes, doable, but it doesn't provide any meaningful protection to anyone creating a promise. Sometime I wish we could have a protected class in the inheritance definition and we could expose it with an eo_protected_get(obj, class) that would return a new Eo_Id, but which would point to the same object. This would solve this case for example, but well, the proposition below is more usable. > 3. If you don't want to restrict yourself to the assumption made in #1, > that the implementer of Owner can be trusted to know what he's doing, > you can create two classes and two interfaces. > abstract Efl.Promise > interface Efl.Promise.Owner, implements Efl.Promise, adds Owner API > interface Efl.Promise.Future, implements Efl.Promise, adds Future APi > class Efl.Promise.Internal, inherits from Promise, implements both > interfaces > You then create the internal one when implementing a promise and return > Owner or Future as the type (so it's an interface, which is very common > in OOP) depending on who is using them. It's a bit more complex and > doesn't add any assurance in C, but make it work as you want in bindings. It does. I think it could be the way to go. Remember that this API is just for C, we do not care about others language. eo_add is not mandatory. So we are not bound to go with eo_add here. We could have an efl_promise_add that actually return an abstracted Efl.Promise.Owner. That class would have an efl_promise_future_get. This could actually work in C. Ok, I think we have a winner for that problem. > With that being said, I think you are just being difficult with this > requirement. People should know not to be dumb. The same way people know > not to call eo_event_callback_call(obj, EO_EVENT_DEL, ...) on an object > randomly. They *can*, but they shouldn't. You can't protect from > everything. I think the first or second approach are just fine. Problem is that saying moving to Eo will make life easier for user and at the same time saying that people should know better to not provide the same requirement is kind of a contradiction... Anyway as said above, this is just for C, so there is no issue to wrap our eo call and hide some of the internal to make it easier, usable and safer. Maybe it was one of the requirement you haven't grasped in that huge storm of email. This API is only to be used directly in C. It only matters to C. Other language already have a native type and we need that to interoperate with our own promise. > I also want to limit the usage. I want them to be used and proved useful > before we start using them everywhere. By useful and used, I mean useful > and used by people other than the creator. We've detected issues with > eina value, and ecore getops long after they were created although their > creators were already using them and said they were good. Same goes for > Eo, that's why we had so many iterations. I would like to have way more > proof and time before I fully commit our new API to a new unproven > concept. Eo proved useful because people were using it even when it was > hard and we told them not to. This is how much value it added to people. > Let's see if promises prove the same. Until then, I say: please, let's > not expose them in our APIs. At this point, it is wishful thinking. We need a pattern for asynchronous behavior and synchronisation. Nobody has come up with a better proposal and most language do support promise in some form for that purpose. Saying you want to limit its usage, only means you don't want asynchronous API. And if you are concerned by the fact that promise is not well used by anyone except its creator, this is pretty true of everything that went in efl 1.18 interface work. There is for most of them even no example code in our tree to validate there usage, because we have no time. So by your argument, we should just not expose our API... To be honest I have advocated that efl interface work should be a beta in 1.18 and only be considered stable in 1.19. That being said, it has been dismissed, so, there will be consequences. -- 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://sdm.link/zohomanageengine _______________________________________________ enlightenment-devel mailing list enlightenment-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-devel