tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Filip Hanik - Dev Lists <devli...@hanik.com>
Subject Re: Proposed simplification of CometEvent
Date Thu, 14 Jun 2007 19:38:04 GMT
Remy Maucherat wrote:
> Filip Hanik - Dev Lists wrote:
>> here we go, some examples
>>
>> http://people.apache.org/~fhanik/tomcat/aio.html#Example%20code%20snippets 
>>
>>
>> and the entire document has been updated to reflect most changes
>> http://people.apache.org/~fhanik/tomcat/aio.html
>
> Here is an alternative version of the examples using the sandbox API 
> to give people an idea, along with some comments. No renaming of 
> sleep/callback, or others at this time. Hopefully, I did not make too 
> many mistakes.
>
> First example, which can enter a busy loop if all events start 
> returning false for isWriteable (which is not very likely, of course):
>
> public class ExampleCometStockStreamer implements CometProcessor {
>   ...
>   public class StockUpdater extends Thread {
>     public void run() {
>       ...
>       StockUpdates[] updates = fetchUpdates();
>       Client[] clients = getClients(updates);
>       for (int i=0; i<clients.length; i++ ) {
>         CometEvent event = client.getEvent();
>         StockUpdates[] clientList = getClientUpdates(client,updates);
>         client.setAndMergeNextUpdates(clientList);
>         if (event.isWriteable()) {
>           byte[] data = getUpdateChunk(client.getNextUpdates());
>           event.getHttpServletResponse().getOutputStream().write(data);
>         }
>       }
>       ...
>     }
>   }
Yes, this difference here is one that I would vouch that the API would 
be explicit, instead of implicit.
compare

        if (event.isWriteable()) {
          byte[] data = getUpdateChunk(client.getNextUpdates());
          event.getHttpServletResponse().getOutputStream().write(data);
        } else {
          event.register(OP_WRITE);
        }

with
        if (event.isWriteable()) {
          byte[] data = getUpdateChunk(client.getNextUpdates());
          event.getHttpServletResponse().getOutputStream().write(data);
        }

the implicit registration for a WRITE event is not made clear by the API 
alone, its something you would have to discover.
and one could look for a use case, where a WRITE event wasnt desired.

>   ...
>   public void event(CometEvent event) throws IOException, 
> ServletException {
>     ...
>     if ( event.getEventType() == CometEvent.EventType.BEGIN ) {
>     } if ( event.getEventType() == CometEvent.EventType.READ ) {
>       //read client Id and stock list from client
>       //and add the event to our list
>       String clientId = readClientInfo(event,stocks);
>       clients.add(clientId, event, stocks);
>     } if ( event.getEventType() == CometEvent.EventType.WRITE ) {
>       //we can now write
>       byte[] data = getUpdateChunk(client.getNextUpdates());
>       event.getHttpServletResponse().getOutputStream().write(data);
>     } else if (...) {
>       ...
>     }
>     ...
>   }
>
> }
>
> What this example should be doing is remove the client from the list 
> when isWriteable returns false, and add it back when it gets a write 
>
> I can translate the second example, but it could lead to an abusive 
> poller use and number of events (all writes are also done 
> synchronously with blocking IO, which never makes sense to me).
>
> public class ExampleCometStockStreamer implements CometProcessor {
>   ...
>   public class StockUpdater extends Thread {
>     public void run() {
>       ...
>       StockUpdates[] updates = fetchUpdates();
>       Client[] clients = getClients(updates);
>       for (int i=0; i<clients.length; i++ ) {
>         StockUpdates[] clientList = getClientUpdates(client,updates);
>         client.setAndMergeNextUpdates(clientList);
>         client.getEvent().callback();
>       }
>       ...
>     }
>   }
now its starting to look funny, In the trunk version of the example, I'm 
interested if the socket buffer is ready to receive data,
but the sandbox version of it simply doesn't care, it just calls for a 
tomcat thread.
sandbox:
  client.getEvent().callback(); -> no guarantee for writeability
trunk:
  client.getEvent().register(OP_WRITE) -> event fires when network 
buffer is ready to receive data.

>   ...
>   public void event(CometEvent event) throws IOException, 
> ServletException {
>     ...
>     if ( event.getEventType() == CometEvent.EventType.BEGIN ) {
>       //configure blocking
>       event.configureBlocking(true);
>     } if ( event.getEventType() == CometEvent.EventType.READ ) {
>       //read client Id and stock list from client
>       //and add the event to our list
>       String clientId = readClientInfo(event,stocks);
>       clients.add(clientId, event, stocks);
>     } if ( event.getEventType() == CometEvent.EventType.CALLBACK ) {
>       Client client = clients.get(event);
>       //we can now write
>       byte[] data = getUpdateChunk(client.getNextUpdates());
>       event.getHttpServletResponse().getOutputStream().write(data);
>     } else if (...) {
>       ...
>     }
>     ...
>   }
>
> }

>
> I think the third example is wrong: there's no reason for isWriteable 
> or isReadable to change its result unless they trigger a large amount 
> of logic and some IO operations. I thought you said it was wrong ;) 
> Also, it will be very vulnerable to busy loops. I can translate it by 
> wrapping the content of for (int j=0; j<clients.size(); j++) { into a 
> try/catch, and removing the calls to isWriteable (which only introduce 
> useless events, and may cause additional busy loops).
>
> Straight translation (since isWriteable will trigger a write event 
> which will flush, it will work, but busy loops are pretty much 
> certain; it also assumes things about the data to read):
>
> public class ExampleAllReadThenWriteComet implements CometProcessor {
>   ...
>   public class AllWriterThread extends Thread {
>     byte[] dataChunks = ...;
>     public void run() {
>       ...
>       for (int i=0; i<dataChunks.length; i++ ) {
>         for (int j=0; j<clients.size(); j++) {
>           boolean done = false;
>           while (!done) {
>             //first read the first request
>             //but only if our previous write was completed
>             if ( clients[j].getEvent().isWriteable() && 
> clients[j].getEvent().isReadable() ) {
>               done = readClientData(clients[j]); //returns true if all 
> data has been received for a request
>             }
>           }
>           done = false;
>           while (!done) {
>             //write the response
>             if ( clients[j].getEvent().isWriteable() {
>
> clients[j].getEvent().getHttpServletResponse().write(dataChunks[i]);
>                done = true;
>             }
>           }
>         }
>       }
>       ...
>     }
>   }
>   ...
>   public void event(CometEvent event) throws IOException, 
> ServletException {
>     ...
>     if ( event.getEventType() == CometEvent.EventType.BEGIN ) {
>       //add the event to our client list
>       clients.add(event);
>       //start our writer if all clients have arrived
>       if (clients.size()==5) {
>         AllWriterThread thread = new AllWriterThread();
>         thread.start();
>       }
>     } if ( event.getEventType() == CometEvent.EventType.READ ) {
>     } if ( event.getEventType() == CometEvent.EventType.WRITE ) {
>     } else if (...) {
>       ...
>     }
>     ...
>   }
>
> }
>
> The last example is quite funny, and I can't translate it (doh), since 
> there's no opposite API to sleep (once you sleep, you have to 
> callback). Waiting until you do operations on a number of connections 
> seems weird, since all would perform as bad as the slowest one (or die 
> if one does not send the data it was supposed to send). I would like 
> to hear about a valid use case (by opposition to crazy coder design) 
> which relies on freely enabling and disabling events.
>
> All of these examples are not very good, I think :( In most of these, 
> you should not be using Comet I think: CPU usage will skyrocket, 
> killing off the benefits over regular Servlets with blocking IO.
Of course the examples aren't very "real world'ish", the point was to 
simply show the differences in the API.

However, I think we have uncovered a larger problem than "what API do we 
pick" right now. The concept of Comet is very clear to the two of us.
I know how your impl would work, and I'm pretty sure you know how mine 
would work. But beyond that, the basics around Comet is still not well 
explained nor understood.
While we are debating method names and implementation details, our 
thread is being misunderstood.

I think starting to work on the Bayeux impl, or some other examples, 
will shine some more light on this.

Filip

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message