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]