Here's some rational for why we'd want to do something like this...

The basic context is that Barracuda is a presentation framework - so we're
trying to make hard things easy for developers.

Given that, one of the things that is a pain in the servlet/webapp paradigm
is handling requests that take a long time to complete (think generating
really big reports, where just the query might take many minutes to execute,
etc). As a developer, what you'd like to be able to do is give the user a)
ability to see progress of the task, b) ability to cancel the task (and
actually have it get killed on the server, rather than just continuing to
run and then write the results to nowhere). So Barracuda's job (in this
particular case) is to make it trivial for a developer to implement a task
like this.

So the gist of what we are doing is this:

a) initial request (req1) comes in, and the framework identifies it as "long
running"

b) framework sends a frameset response back to the client which forks the
initial request into 2 secondary requests, one to actually perform the
long-running functionality (req2a), the other to show the user progress and
give him a link to cancel the task (req2b)

c) when req2a gets to the point of actually having data ready to write, it
can't just spit it back to the client (because of the frameset) - so it
sends a response back to the client saying "redirect the whole browser to
get your data" (req3), and then req2a continues to write the data, but the
response object its writing to is actually a wrapper over a piped
reader/writer (which writes the data to a buffer in memory)

d) when req3 comes in, it locates the data structure that req2a is writing
to, and reads data out of that structure back to the req3 response

Now, a word about our piped reader/writer - one of the things that happens
is rather than writing the whole document (which could get quite large) into
memory, the basic notion of a piped reader/writer is that the writer will
only write until the pipe is full, at which point, the thread sits and spins
until the reader takes some data back out of the pipe. We've added the
notion of a timeout, so that if the client request to get data never comes,
the thread will eventually clean itself up and make itself available again.

So the basic premise here is that its ok to block req2a from writing when
the pipe fills up, because req3 is already on its way, and that's going to
start taking data out of the pipe (thereby allowing req2a to start back up
again).

This works perfectly about 99% of the time. What we are observing is that
there are certain situations where we encounter a deadlock scenario (and
that's what prompted my original question). Basically, req2a writes the
redirect back to the client, the client receives the redirect and actually
sends a request back to the server (verified this by using a proxy to
examine what is getting sent)... BUT - if req2a is already in the blocking
code, tomcat simply does not accept req3 until the blocking code times out!
Bad.

Now what's interesting is that tomcat itself is not blocked at this point -
I can send requests from other browsers, and those get handled just fine. So
something seems to be happening where Tomcat cannot accept this particular
request (req3), perhaps because its still trying to keep the physical
connection associated with req2a open(?) (I'm thinking keep-alives here...in
other words, if the browser is using keep-alives, req3 will be coming across
the same physical connection as req2a, so its almost like tomcat thinks it
still can't receive a new request across this connection, although the
browser thinks its fine to go ahead and send it). But after req2a writes the
redirect response to the browser, I close the writer and flush the buffer -
there's nothing else I can do to say "hey, I really am done with that
response" (even though I'm going to continue running in the background on
the server and writing additional data - the real response, the report - to
a memory structure).

SO...that's basically the gory details of what we're doing and why. I
believe its a legitimate design goal. The alternative (and perhaps
more-technically-proper way, is to create a whole separate threadpool, and
then try to execute the long running process in a background thread that is
distinct from the servlet thread. But that ends up being quite complex as
well, and may in fact be more problematic because the code being executed
needs to look like (to the developer) as if its running in a regular servlet
thread - it needs to have access to the req/response objects, etc. So our
solution was just to use the existing servlet thread, but to write the
response very early on in the process, rather than waiting until the end
(ie. we'll allow a separate request to actually get the end result data).

One easy solution is just to modify our piped reader/writer so that when the
pipe size gets maxed it simply expands the size of the pipe. This would mean
that we'd never actually have to block, so we'd never hit our deadlock. The
downside is that it now means that in some cases we might be significantly
increasing the size of our memory footprint for the server. Another option,
I suppose, would be to simply pipe the document to disk, rather than storing
it to memory.

I can (and will) look at both of these if need be, but first I'd prefer to
hear if there is something obvious that exlains the behavior of tomcat - I'd
really like to understand why what we're doing only deadlocks in a very
small percentage of our cases. I'd like to understand why our blocking code
does not allow tomcat to continue (as I'd expect), and why tomcat can't turn
around and accept req3 from the browser in this situation.

Long answer to a short question, but hopefully that helps flesh things out.

THanks much,
CHristian
----------------------------------------------
Christian Cryder
Internet Architect, ATMReports.com
Project Chair, BarracudaMVC - http://barracudamvc.org
----------------------------------------------
"Coffee? I could quit anytime, just not today"


> -----Original Message-----
> From: Edson Alves Pereira [mailto:[EMAIL PROTECTED]
> Sent: Friday, March 19, 2004 8:00 AM
> To: 'Tomcat Users List'
> Subject: RE: thread deadlock problem
>
>
>       Why are you trying to do this kind of control?
>
> > ----------
> > De:         Christian Cryder[SMTP:[EMAIL PROTECTED]
> > Responder:  Tomcat Users List
> > Enviada:    sexta-feira, 19 de março de 2004 9:55
> > Para:       Tomcat-User
> > Assunto:    thread deadlock problem
> >
> > Hi folks,
> >
> > I need to know if someone can explain the following behavior:
> >
> > 1. client browser issues a request
> > 2. tomcat servlet code starts handling the request...
> >     a. writes an html redirect to the resp, flushes the buffer, etc
> >     b. thread continues processing (writing to a data structure)
> > 3. client browser receives 2a response and generates another request...
> >     a. reads data out of the data structure populated by 2b
> >
> > What's happening is that 2b fills up the structure and then blocks,
> > waiting
> > until 3a reads some of the data out, so that it can continue.
> The blocking
> > code looks like this:
> >
> >     ...check to see if data pipe still full
> >     ...timeout if we've waited too long
> >
> >     notifyAll();
> >     try {
> >         wait(1000);
> >         Thread.yield();
> >     } catch (InterruptedException ex) {
> >         throw new java.io.InterruptedIOException();
> >     }
> >
> > Now what is happening is that in certain situations, the request for 3a
> > never gets accepted by the server until 2b times out. I'm trying to
> > understand why (and what to do about it). I have verified that
> the client
> > not only receives the response from 2a, but it actually issues
> the request
> > for 3a. Nevertheless, once Tomcat is in this blocking code
> (above) it does
> > not seem to accept requests from this particular browser window, -UNTIL-
> > 2b
> > times out.
> >
> > Can anyone explain this to me? Should I be blocking/yielding in
> some other
> > fashion? Why won't Tomcat accept my subsequent requests when I'm in this
> > blocking code?
> >
> > What I'm looking for more than just "that's a stupid thing to do" - I'm
> > hoping someone can either
> >
> > a) explain the nitty-gritty of how tomcat handles writing the
> code back to
> > the browser (and telling the browser that everything is complete) in the
> > case where the thread may actually need to continue running for a longer
> > period of time -or-
> >
> > b) offer some constructive suggestions as to where I should
> start looking
> > in
> > the tomcat code to answer those questions myself
> >
> > Any suggestions would be greatly appreciated...
> >
> > tia,
> > Christian
> > ----------------------------------------------
> > Christian Cryder
> > Internet Architect, ATMReports.com
> > Project Chair, BarracudaMVC - http://barracudamvc.org
> > ----------------------------------------------
> > "Coffee? I could quit anytime, just not today"
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: [EMAIL PROTECTED]
> > For additional commands, e-mail: [EMAIL PROTECTED]
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: [EMAIL PROTECTED]
> > For additional commands, e-mail: [EMAIL PROTECTED]
> >
>


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to