dev@grizzly.java.net

Re: Meeting Minutes, Oct 24, 2007

From: Harsha Godugu <Harsha.Godugu_at_Sun.COM>
Date: Wed, 24 Oct 2007 14:52:20 -0700

----- 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.

>
>
> 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.

>
> 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.

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.


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. The worker threads process each callable in the queue, by checking the callback handler first. 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.

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.

thanks,
Harsha