Skip to content

Problem with lambda properties and threading #2

@cware-architect

Description

@cware-architect

First, I was having issues with the OutputToReadStream.wrap( input ) method, I was trying to perform a proper close on the stream. But you can't close the output stream until the operation is complete. Therefore, the close() must be called on the future from pipeTo(...) inside onComplete( x-> {} ).

First, is there a cleaner way to close the output stream? You can't use the try-resource block because it closes the output too soon.

final OutputToReadStream os = new OutputToReadStream( vertx );
final Future<Void> pipeTo = os.wrap( image ).onComplete( x -> {
              try {
                os.close();
                }
              catch( Exception e ) {
                e.printStackTrace();
                }
          } );

The issue with this code is that onComplete is executed back on a vertx loop thread! And because close() makes an await call, this is bad. That means extra care must be taken to call close() on a different thread, like this...

final OutputToReadStream os = new OutputToReadStream( vertx );
final Future<Void> pipeTo = os.wrap( image ).pipeTo( ctx.response() )
      .onComplete( x -> {
            // must FJP close because onComplete is vertx loop thread!
            ForkJoinPool.commonPool().submit( () -> {
              try {
                os.close();
                }
              catch( Exception e ) {
                e.printStackTrace();
                }
              } );
          } );

*** But there's an underlying bug in the push( null ) called by close(). I think it was exasperated by the use another thread. It's throwing a NPE on this line...

        if( data == null )
           endHandler.handle( null );   <--- NPE
        else
          dataHandler.handle( data );

endHandler is null. The endHandler property is only set by one method endHandler(...) and it appears there is a call to it from the .pipeTo(...) method. The PipeImpl constructor is setting it to null. This contradicts your expected empty lambda default of x->{}

I put a null check before the call to .handle( null ), but there may be other places that need this too. Or, you can reject nulls, but that will break the PipeImpl.

I chose to replace any null settings with the empty lambda x->{} so as to keep with your initialization assumptions. This code works for me...

  @Override
  public OutputToReadStream endHandler( Handler<Void> endHandler ) {
    this.endHandler = endHandler == null ? x -> {  } : endHandler;
    return this;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions