dev@grizzly.java.net

Re: Meeting Minutes, Oct 24, 2007

From: Harsha Godugu <Harsha.Godugu_at_Sun.COM>
Date: Wed, 24 Oct 2007 13:47:16 -0700

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 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. If there is no read event, it fallse to else block as I mentioned above and executes the read filter. 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.

Harsha.