users@servlet-spec.java.net

[servlet-spec users] [jsr340-experts] Re: Async IO and Upgrade proposal updated

From: Greg Wilkins <gregw_at_intalio.com>
Date: Mon, 2 Apr 2012 22:59:25 +1000

Oleksiy,

The proposal that you make is essentially the NIO.2 style, where the
buffer is passed to the IO layer and is asynchronously flushed and
once it is done you can either ask if it is complete or have a
callback telling you that it is complete.

However, your API does have some benefits over NIO.2 - it allows a
poll to test for completion before committing to register a callback
handler, which may avoid some of the expense of callback.

I think we could build on your approach, but give it more NIO.2 syntax:

  interface IOFuture extends Future<Integer>
  {
     setCompletionHandler(CompletionHandler<Integer,Object> handler);
  }

Write operations could then return an IOFuture which could be queried
for completion (equivalent to your canWrite call) or have a completion
handler registered (equivalent to your os.notifyCanWrite).

Eitherway, I agree with your basic premise - that we pass the entire
application buffer to the IOLayer and let it do as much work as
possible without copying - and then communicate back to the
application when the previous operation is complete and the buffer can
be reused.

cheers







On 2 April 2012 19:06, Oleksiy Stashok <oleksiy.stashok_at_oracle.com> wrote:
> Hi Greg,
>
>
> On 04/02/2012 07:10 AM, Greg Wilkins wrote:
>
> On 31 March 2012 05:46, Remy Maucherat <rmaucher_at_redhat.com> wrote:
>
> On Fri, 2012-03-30 at 19:23 +0200, Oleksiy Stashok wrote:
>
> Once we don't have integer parameter neither in canWrite nor in
> notifyCanWrite, AFAIU we can guarantee that if canWrite() returned
> true - next "write" method call (despite the size of the passed data)
> will not block.
>
> That's what I am doing (and it works). It is necessary IMO since the for
> example char methods of the writer are relatively unpredictable in terms
> of output size. The downside is the resource use, but as you mention,
> regardless of the technique employed, the memory is used until the bytes
> are written.
>
> There is a big difference between the application holding the
> reference and the IO layer holding the reference. The application
> layer can know if the buffer is shareable or not.
>
> Say I have 100,000 connections and I want to write the same large
> string to all of them, then if the application holds the reference,
> then it converts the ig string to a ByteBuffer it can pass a
> buffer.asReadOnly() to each connection and there is no copy. If the
> IO layer holds the reference, then it will have to copy it (all of it
> or just the remainder), then we get 100,000 copies of the large
> string!
>
> I agree, but it's not what I tried to propose. I proposed to introduce
> separate (Servlet)OutputStream.write(ByteBuffer) method, which will be
> copy-free. It's going to complicate the ByteBuffer re-usage, but IMO it's
> worth it.
>
> Here is the sample I gave in the prev. email:
>
> -------------------------------------------------------------------
> if (os.canWrite()) {
>         os.write(appBuffer);
> }
>
> os.flush();       <---------- flush servlet buffer
>
> if (os.canWrite()) {
>     (1) <----------------  starting here we can reuse appBuffer
> } else {
>         os.notifyCanWrite(new WriteListener() {
>                     void onWritePossible() {
>                                 (2) <----------------  starting here we can
> reuse appBuffer
>                     }
>         }
> }
> -------------------------------------------------------------------
>
> WBR,
> Oleksiy.
>