dev@grizzly.java.net

Re: Meeting Minutes, Oct 24, 2007

From: Harsha Godugu <Harsha.Godugu_at_Sun.COM>
Date: Wed, 24 Oct 2007 15:37:33 -0700

Hi Jeanfrancois,

Thanks for the quick response. I've my comments inlined.

However, at present I do not have any roadblocks in Grizzly for Corba work, as I'm using the workaround.

thanks,
Harsha

----- Original Message -----
From: Jeanfrancois Arcand <Jeanfrancois.Arcand_at_Sun.COM>
Date: Wednesday, October 24, 2007 3:06 pm
Subject: Re: Meeting Minutes, Oct 24, 2007
To: dev_at_grizzly.dev.java.net


> Hi Harsha,
>
> Harsha Godugu wrote:
> >
> > ----- Original Message -----
> > From: Jeanfrancois Arcand <Jeanfrancois.Arcand_at_Sun.COM>
> > Date: Wednesday, October 24, 2007 2:23 pm
> > Subject: Re: Meeting Minutes, Oct 24, 2007
> > To: dev_at_grizzly.dev.java.net
> >
> >
> >> Harsha Godugu wrote:
> >>> Thanks Charlie for the meeting minutes. Of all, the one interest
> to
> >> me, in this email is about the read filters. So, I'm erasing the
> rest
> >> of the line, to just to focus on read filters.
> >>>>> Charlie asked about how during default ReadFilter works once it
>
> >>>>> receives an OP_READ event until it re-enables interest ops on
> the
> >>>>> SelectionKey. In particular, under what circumstances does
> >> default
> >>>>> ReadFilter do multiple reads?
> >>>>>
> >>>>> Looking a ReadFilter.execute(), we see:
> >>>>>
> >>>> // As soon as bytes are ready, invoke the next ProtocolFilter.
> >>>> while (channel.isOpen() && (count = channel.read(byteBuffer)) ==
> 0){
> >>>> // Avoid calling the Selector.
> >>>> if (++loop > 2){
> >>>> if (ctx.getKeyRegistrationState() !=
> >>>> Context.KeyRegistrationState.NONE){
> >>>> ctx.setAttribute(ProtocolFilter.SUCCESSFUL_READ, Boolean.FALSE);
> >>>> invokeNextFilter = false;
> >>>> }
> >>>> break;
> >>>> }
> >>>> }
> >>>>> Hence, we see that Grizzly will read once if it there are bytes
>
> >> read
> >>>>> from the channel. It will try a total of 3 times before giving
>
> >> up.
> >>>>> Also notice that if it reads 3 times and has found no bytes have
>
> >>>> been
> >>>>> read, the 'invokeNextFilter' is set to false. So this means if
> >> bytes
> >>>>> had been read, the next Filter in the chain is invoked. If not,
>
> >> the
> >>>>> ReadFilter will not invoke the next Filter. In both cases, when
>
> >> the
> >>>>> chain of filters have finished executing, the interest op will
> be
> >>>>> re-enabled on the SelectionKey
> >>> Let's go back a step and get little context.
> >>>
> >>> In corba the requirement is to use the same protocol filter on
> both
> >> client and serve side, so that when there is a need to parse the
> >> received bytes, we can use the same filter and parser mechanism.
>
> >> That requirement lead us to use the filter from the callback
> handler
> >> mechanism in Grizzly. That means, when there is read event, it
> will
> >> invoke the read filter that reads the available bytes and then
> gives
> >> control to the parser. Thats fine until this stage. In order
> >> accomplish this, we all know that, we need to add the given
> protocol
> >> filter (in this case read filter) to the protocol chain.
> >>> We accomplish invoking the filters from callback handler by the
> code
> >> like the following:
> >>> public void onRead(IOEvent<Context> ioEvent) {
> >>> try {
> >>> Context ctx = ioEvent.attachment();
> >>> SelectionKey key = ioEvent.attachment().getSelectionKey();
> >>> // disable OP_READ on key before doing anything else
> >>> key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
> >>> ctx.getProtocolChain().execute(ioEvent.attachment());
> >>> ...
> >>> ..
> >>> //enable read op here or other place once reading
> is
> >> done
> >>> }
> >>> }
> >>>
> >>> Now, the issue/bug (to me at-least) is, take a look at the
> following
> >> code from : Context.java which is the heat of the Grizzly
> protocol
> >> filters and callback handlers. The method we are interested in
> here is:
> >>> public Object call() throws Exception {
> >>> // we are interested in the if /else block
> >>> if (ioEvent != null && (attachment instanceof CallbackHandler)){
> >>> ... ...
> >>> // not copied entire code for saving space.
> >>> } else {
> >>> SelectionKey currentKey = key;
> >>> selectorHandler.getSelectionKeyHandler().process(currentKey);
> >>> try {
> >>> protocolChain.execute(this);
> >>> } finally {
> >>> selectorHandler.getSelectionKeyHandler().postProcess(currentKey);
> >>> }
> >>> }
> >>>
> >>> }
> >>>
> >>> From the code above, we note that, if there is a callback handler,
>
> >> the control (while in a worker thread, does not matter here) falls
>
> >> into the protocol chain and then handles the read event in the
> >> protocol filter (here its is read filter) . That's fine no problems
>
> >> there. The issue is around the ELSE block.
> >>> Assume the scenario of, a non read event.. during that time
> >> Here you means a write event? You can always query the Context to
> >> learn
> >> what is the type of OP currently processed (OP_READ or OP_WRITE).
> >>
> >> the IF condition fails and then gets into ELSE case and executes
> the
> >> protocol filter (read filter). This where, I was referring
> multiple
> >> reads.
> >>> Now imagine a case, we have a read filter, getting invoked
> through
> >> the callback handler on a connection.
> >> The filters get executed by default whenever a target callable
> is
> >> added to the worker queue.
> >> Each time a worker thread executes the callable, it check for the
>
> >> callback handler and read event.
> >>
> >> Can you elaborate on the type of event here? You means a write
> event
> >> as
> >> well (like above)?
> >
> > Yes. Any even thats other than read.
>
> So OP_WRITE (not OP_CONNECT I suppose).
>
> >
> >>
> >> If there is no read event, it fallse to else block as I
> mentioned
> >> above and executes the read filter.
> >>
> >> Are you reading bytes inside the CallbackHandler? If yes, then it
> will
> >>
> >> explain why you are reading 0 bytes.
> >
> > Yes.
>
> OK so you read bytes inside the CallbackHandler and then invoke the
> ReadFilter. That's explain why the ReadFilter cannot read bytes.
> Should
> you avoid calling the ReadFilter in that case?
>
> >
> >> This where I was seeing multiple reads. I worked around this by
>
> >> overriding the following method in TCPSelectorHandler.java
> >>> public boolean onReadInterest(final SelectionKey key, final
> Context
> >> ctx) throws IOException {
> >>> // code to add a callback handler
> >>> // return always false, so that, we do not fall into
> the
> >> protocol chain
> >>> }
> >>>
> >>> With the above change , I do NOT see multiple reads through the
> read
> >> filter.
> >>
> >> If you override that method, then your CallbackHandler will never
> be
> >> invoked. So why using CallbackHandler then? For handling the
> OP_WRITE?
> >>
> >> That would make sense if true, but I suspect you should make a
> clear
> >> distinction between OP_READ and OP_WRITE.
> >
> > To be clear, here is how the over-ridden method looks like: in TCPSelectorHandler.java
> >
> > public boolean onReadInterest(final SelectionKey key, final
> Context ctx) thr
> > ows IOException {
> >
> > //return super.onReadInterest(key, ctx);
> > key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
> > key.attach(createCB(ctx.getController()));
> > {
> > final Context context = ctx.getController().pollContext(key);
> > context.setSelectionKey(key);
> > context.setCurrentOpType(Context.OpType.OP_READ);
> > invokeCallbackHandler(context);
> > return false;
> > }
> > }
> >
> > This works for both on client side and serveside, as far as the
> protocol filters are concerned.
> > Please note that, the way we wanted to invoke protocol filter (read)
> is :
> > i) to use the same filter on both client and server side
> > ii) to use the read filter ONLY when there is signal from
> selector that there is a READ op.
>
> yes same as Sailfin (SIP protocol :-)). I'm trying to understand why I
>
> didn't have to override the method.
>
> >
> > In order to avoid falling into the protocol (read) chain, in case
> of NON READ operation, the above overriden method
> > returns false ALWAYS. This is a special case may be.
>
> Yes.
>
> >
> >
> > Please correct me in the following lines:
> >
> > Grizzly's protocol filters are aimed at handling processing
> requirements of data (read/write/process) irrespective if what the
> selector signals of any event type on designated channel.
>
>
> False :-) The ReadFilter is for read operations, not for write (both
> TCP
> and UDP).

Thats what I was expecting. A read filter for read operations. But it does not seem like, from the code as I mentioned.
Sorry, its very complex to describe in an email. But, i will try.

>
> The worker threads process each callable in the queue, by checking the
>
> callback handler first.
>
> Only if you need to use the client side of Grizzly. If you aren't
> using
> the client side (like opening a connection to a remote server), then
> the
> CallbackHandler should not be used (as an example, the Grizzly http
> server isn't using CallbackHandler, as it it a server side component).

This was the constraint for Grizzly a long time ago.
Ok, I will ask you the other way. I've a filter and parser. I wanted to use it (the SAME code, logic ) on both client and server. If I use readFilters on the serverside and
callbackhandlers on the client side, I BUMP into multiple reads out of which some of the reads are FALSE and the parser gets into a LOOP.

Please read again, my first email and the description and Context.java file, in the context of a single filter acting as server and client in the same process.

>
> Sailfin (SIP) use the client side to connect to remote client.
>
> Is Corba needs to connect (meaning opening a new connection)? My
> understanding was no, you don't.

Yes. On the client side.

>If that's the case, then you should
> not
> use CallBackHandler. Does it make sense?
>
> If none found, then, for the case of read filter, we go and read
> on
> a channel as quickly as possible. Grizzly makes an attempt to read.
>
> The ProtocolChain (and its ProtocolFilter) will always be called when
>
> there is either an OP_READ or OP_WRITE. If you use Grizzly client,
> then
> you decide by yourself (inside the CallbackHandler) to invoke the
> ProtocolChain. In your case, when CallbackHandler.onWrite() is called,
>
> you should not call the ProtocolChain as the ReadFilter will be invoked.
>
>
> >
> > Is that correct?
> >
> > Corba's parser is so sensitive that, when controls falls here, it
> ASSUMES that the underlying channel is ready to read. If it DID not
> read any bytes (zero count) then, the parser assumes that, the
> previous read is unsuccessful and hence gives back control to
> Grizzly's read filter to re-try the read, by setting the parser
> boolean isExpectingMoreData to true. For this reason, if there
> happened a non-read, which might be of no interest, at that time, if
> the control falls into
> > filter then attempts to READ (unnecessary read) quickly and then
> returns ZERO count and invokes parser with that bytebuffer with zero
> fresh bytes, then parser assumes, wrongly that, the underlying
> channel is ready to read and the first read was unsuccessful. This
> was the scenario that I bumped into recenlty.
> >
>
> But if you execute a read inside the CallbackHandler, then for sure
> the
> ReadFilter will always fail. This is the part I don't understand. If
> you
> know the read will fail (or if ou are handling an OP_WRITE), why
> invoking the ProtocolChain/ReadFilter?

I do not belive read is failing AFTER making the changes mentioned here. Before. these changes, I was getting into multiple reads.
Some of the reads were FALSE reads. Then I digged into this part of code in Grizzly. And then, came up with these suggestions.
The logic is, we will have a protocol parser executed in a Callback handler for reads alone, when a read occurs.


> Sorry for asking so many questions, but I really want to understand
Sure. Pl. feel free. I too want to make sure I'm doing the right approach.

Please note that, all this discussion started (2 weeks ago) and arrived at a workaround mainly due to the fact I was seeing mutliple reads from Grizzly. In order to overcome that, I came up with the workaround.

You are aslo right on the part that, we do not have to use ReadFilter at all. I can just use a callback handler for the channel(s), and upon read event,
I can directly call, the parser. But, the motive behind is, we wanted to use very less code of our own and use Grizzly to the full potential. That's what
Ken C. has been preaching me :-) .

thanks,
Harsha