Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let OpenSslContext implement ReferenceCounted? #4958

Closed
rkapsi opened this issue Mar 10, 2016 · 7 comments
Closed

Let OpenSslContext implement ReferenceCounted? #4958

rkapsi opened this issue Mar 10, 2016 · 7 comments

Comments

@rkapsi
Copy link
Member

rkapsi commented Mar 10, 2016

Hello,

would it make sense to let the OpenSslContext class implement the ReferenceCounted interface?

We have a dynamic and unbound set of OpenSslContext that are concurrently in use and it would be great if we can release the native resources in a timely manner. We're currently using a wrapper object that extends SslContext, implements ReferenceCounted and gets passed into the SniHandler.

Something along the lines of this pseudo code. I'd be happy to open a PR.

public class OpenSslContext extends SslContext implements ReferenceCounted {

  private final ReferenceCounted refCnt = new AbstractReferenceCounted() {

    @Override
    public ReferenceCounted touch(Object hint) {
      OpenSslContext.this.hint = hint;
      return this;
    }

    @Override
    protected void deallocate() {
      destroy();
    }
  };

  private Object hint = null;

  @Override
  public int refCnt() {
    return refCnt.refCnt();
  }

  @Override
  public OpenSslContext retain() {
    refCnt.retain();
    return this;
  }

  @Override
  public OpenSslContext retain(int increment) {
    refCnt.retain(increment);
    return this;
  }

  @Override
  public OpenSslContext touch() {
    refCnt.touch();
    return this;
  }

  @Override
  public OpenSslContext touch(Object hint) {
    refCnt.touch(hint);
    return this;
  }

  @Override
  public boolean release() {
    return refCnt.release();
  }

  @Override
  public boolean release(int decrement) {
    return refCnt.release(decrement);
  }

  protected final void destroy() {
    synchronized (OpenSslContext.class) {
      if (ctx != 0) {
        SSLContext.free(ctx);
        ctx = 0;
      }

      // Guard against multiple destroyPools() calls triggered by construction exception and finalize() later
      if (aprPool != 0) {
        Pool.destroy(aprPool);
        aprPool = 0;
      }
    }
  }
}
rkapsi pushed a commit to Squarespace/netty that referenced this issue Mar 11, 2016
[netty#4958]: OpenSslContext retains native resources that get released by the
Garbage Collector via object finalization. This works fine for traditonal SSL
deployments with static working sets of SslContext(s). We intend to load large
sets of [Open]SslContext(s) on a per connection basis.

Having OpenSslContext implement the ReferenceCounted interface would
facilitate efficient sharing and timely relase of all native resources once all
client disconnected that used the same context.

Modifications

Let OpenSslContext implement the ReferenceCounted and delegate all calls
to an inner AbstractReferenceCounted field that calls OpenSslContext's destroy()
method once the reference count reaches 0.

Result

It's possible to release OpenSslContext's native resources as soon as it's
no longer in use.
@normanmaurer
Copy link
Member

@rkapsi I think we could do this. That said we would still need a finalizer for the OpenSslEngine itself as we never know what the user will do with it (or will use it at all after creating).

@rkapsi
Copy link
Member Author

rkapsi commented Mar 14, 2016

@normanmaurer Correct. It's meant to be an optimization for folks who don't want to wait for the GC. The finalizer will still run but potentially do nothing.

Scottmitch added a commit to Scottmitch/netty that referenced this issue Aug 5, 2016
Motivation:
OpenSslEngine and OpenSslContext currently rely on finalizers to ensure that native resources are cleaned up. Finalizers require the GC to do extra work, and this extra work can be avoided if the user instead takes responsibility of releasing the native resources.

Modifications:
- Make a base class for OpenSslENgine and OpenSslContext which does not have a finalizer but instead implements ReferenceCounted. If this engine is inserted into the pipeline it will be released by the SslHandler
- Add a new SslProvider which can be used to enable this new feature

Result:
Users can opt-in to a finalizer free OpenSslEngine and OpenSslContext.
Fixes netty#4958
Scottmitch added a commit that referenced this issue Aug 5, 2016
Motivation:
OpenSslEngine and OpenSslContext currently rely on finalizers to ensure that native resources are cleaned up. Finalizers require the GC to do extra work, and this extra work can be avoided if the user instead takes responsibility of releasing the native resources.

Modifications:
- Make a base class for OpenSslENgine and OpenSslContext which does not have a finalizer but instead implements ReferenceCounted. If this engine is inserted into the pipeline it will be released by the SslHandler
- Add a new SslProvider which can be used to enable this new feature

Result:
Users can opt-in to a finalizer free OpenSslEngine and OpenSslContext.
Fixes #4958
liuzhengyang pushed a commit to liuzhengyang/netty that referenced this issue Sep 10, 2017
Motivation:
OpenSslEngine and OpenSslContext currently rely on finalizers to ensure that native resources are cleaned up. Finalizers require the GC to do extra work, and this extra work can be avoided if the user instead takes responsibility of releasing the native resources.

Modifications:
- Make a base class for OpenSslENgine and OpenSslContext which does not have a finalizer but instead implements ReferenceCounted. If this engine is inserted into the pipeline it will be released by the SslHandler
- Add a new SslProvider which can be used to enable this new feature

Result:
Users can opt-in to a finalizer free OpenSslEngine and OpenSslContext.
Fixes netty#4958
@johnou
Copy link
Contributor

johnou commented Sep 29, 2019

@rkapsi are you using the OPENSSL_REFCNT ssl provider in production? I'm currently trying to wrap my head around the usage eg. every call to io.netty.handler.ssl.SslContext#newEngine(io.netty.buffer.ByteBufAllocator) must be matched with releasing it via io.netty.util.ReferenceCountUtil#release(java.lang.Object) or is it enough just to release the io.netty.handler.ssl.SslContext? equally from a client pov it seems it is possible to hack the openssl_refcnt ssl provider into async-http-client using a custom org.asynchttpclient.SslEngineFactory but I suppose each connection may need to be released explicitly too.

@rkapsi
Copy link
Member Author

rkapsi commented Sep 30, 2019

@johnou we do. In our case we match newEngine(...) to one release(...) upon the client's disconnect.

Management of the SslContext follows a similar pattern but there is a little twist. We try to re-use an SslContext instance for as long as possible (with constraints) by making use of a (heap) caching layer. The flow looks about like this:

A client connects and we go thru the SNI flow. The SNI thingy is backed by the said cache. The cache always increments the refCnt by 1 before returning it and we then do newEngine(...). When the client disconnects we decrement the engine's as well as the context's refCnts by 1. And lastly the cache decrements the context's refCnt upon eviction.

To put it differently, a context's refCnt will always be >= 1 as long as a reference is being held in the cache OR one connection is using it.

@johnou
Copy link
Contributor

johnou commented Sep 30, 2019

@rkapsi perfect, thanks! I am currently investigating implementing it for our server application as we have thousands and thousands of concurrent long lived SSL connections.. at first glance it looks like I don't even need to track the engine as releasing it is already taken care of here io.netty.handler.ssl.SslHandler#handlerRemoved0 😀

@rkapsi
Copy link
Member Author

rkapsi commented Sep 30, 2019

Yea, we do something like this:

class MyHandler extends SslHandler {
  SslContext sslContext;

  void handlerRemoved(...) {
    try {
      super.handlerRemoved(...);
    } finally {
      sslContext.release();
    }
  }
}

@njhill
Copy link
Member

njhill commented Oct 1, 2019

See also #9626

pulllock pushed a commit to pulllock/netty that referenced this issue Oct 19, 2023
Motivation:
OpenSslEngine and OpenSslContext currently rely on finalizers to ensure that native resources are cleaned up. Finalizers require the GC to do extra work, and this extra work can be avoided if the user instead takes responsibility of releasing the native resources.

Modifications:
- Make a base class for OpenSslENgine and OpenSslContext which does not have a finalizer but instead implements ReferenceCounted. If this engine is inserted into the pipeline it will be released by the SslHandler
- Add a new SslProvider which can be used to enable this new feature

Result:
Users can opt-in to a finalizer free OpenSslEngine and OpenSslContext.
Fixes netty#4958
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants