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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to