- From: Olli Pettay <Olli.Pettay@helsinki.fi>
- Date: Thu, 06 Sep 2012 11:57:38 +0300
- To: Jonas Sicking <jonas@sicking.cc>
- CC: Glenn Maynard <glenn@zewt.org>, Andrea Marchesini <amarchesini@mozilla.com>, David Bruant <bruant.d@gmail.com>, "public-webapps@w3.org" <public-webapps@w3.org>
On 09/06/2012 05:03 AM, Jonas Sicking wrote: > On Wed, Sep 5, 2012 at 12:49 AM, Jonas Sicking <jonas@sicking.cc> wrote: >>> Importantly, the sending side >>> doesn't have to know whether the receiving side is using a sync API to >>> receive it or not--in other words, that information doesn't have to be part >>> of the user's messaging protocol. >> >> I agree that this is desirable trait. But so far I think the risks in >> encouraging event loop spinning outweigh the benefits. > > I did some more thinking about this and came to the following conclusions: > > The part that I dislike about having single channel used for both sync > and async messaging is that you end up with one or more async > listeners which expect to get notified about all incoming messages, > but then you have an API which "steals" a message away from those > listeners. On top of that it has to do that stealing without any way > of ensuring ensuring that it actually steals the right message. It has > to simply rely on prior information and hope that no unexpected > messages sneak in where not expected. > > Basically any time you use the sync API to pull out a message and get > one that wasn't exactly the one you wanted, you have lost. I think all > solutions for dealing with that situation are too brittle and all have > side effects which will behave very intermittently and result in > hard-to-find bugs. > > However, I do like the idea of the async side of a channel not caring > about if the other side is synchronous or not. Though no matter what > we do, the async side can't be completely agnostic to if the other > side uses blocking reads or not since if the other side wants to use > blocking reads, the async side is limited in where it can pass its > port. > > But being (mostly) agnostic to if the other side is using sync > messages or not doesn't mean that the other side uses both sync and > async messaging! So what we could do is to create a SyncMessageChannel > where the async port looks exactly like the port of a normal > MessageChannel. That way a worker (or main thread) which provides an > asynchronous API by receiving a port to communicate to, can with no > effort both accept the port of a MessageChannel as well as the async > side of a SyncMessageChannel. > > Hence I think something like the following would work: > > [Constructor] > interface MessageChannel { > readonly attribute MessagePortSyncSide syncPort; > readonly attribute MessagePortAsyncSide asyncPort; > }; > > interface MessagePortSyncSide { > void postMessage(any message, optional sequence<Transferable> transfer); > any waitForMessage(); > void close(); > }; > MessagePortSyncSide implements Transferable; > > interface MessagePortAsyncSide : EventTarget { > void postMessage(any message, optional sequence<Transferable> transfer); > void start(); > void close(); > > // event handlers > attribute EventHandler onmessage; > }; > MessagePortAsyncSide implements Transferable; > > > Where there's the additional limitation that MessagePortSyncSide can > only be transferred though MessagePortAsyncSide.postMessage() or > Worker.postMessage(), and MessagePortAsyncSide can only be transferred > though MessagePortSyncSide.postMessage() or > DedicatedWorkerGlobalScope.postMessage(). > > Note that even MessagePortAsyncSide is fully API compatible with the > normal MessagePort, but code can still differentiate between the two > by using the instanceof operator or Object.toString(). > > I'm still not convinced that this is a better API than the ones > proposed in Olli's proposal 1 and proposal 2, but I think it's an > option. I think I prefer your approach over the proposals 1 and 2 because it makes it clear that it is all about different communication channel so it should be more obvious to the API user that data ordering can't be guaranteed. So, I might be able to live with your proposal. Although, merging SyncMessageChannel and proposal 2 might be good. Since if we're giving up with the issues with data ordering, we could give up with waitForMessage too and have something like syncPort.postSyncMessage(); which would block the caller until reply is received. However, that would effectively make the channel request-response only, not truly bi-directional. But I think that is a good thing if MessagePortSyncSide doesn't have any callbacks for handling incoming messages. As a syntactic sugar, something like this could be nice var c = new SyncMessageChannel(); // send ports to the right places asyncPort.start({ doSomething: function(message) { // do something with message.data message.reply("did something"); }, doSomethingElse: function(message) { // do something with message.data message.reply("did something else"); } }) var returnValue1 = syncPort.doSomething("data1"); var returnValue1 = syncPort.doSomethingElse("data1"); > > What I like about proposals 1 and 2 is that they optimize for the > common case of simply wanting to perform a function call in a > different thread and waiting for an answer. We could even add a > .throw() function in addition to .reply() which makes the caller throw > an exception, very similar to ES6's generator.throw(). > > / Jonas >
Received on Thursday, 6 September 2012 08:58:19 UTC