Repository: dotnet/runtime
File: src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs (C#)

Code before:

        private readonly SafeSslHandle _sslContext;

        private GCHandle _gcHandle;

        private readonly object _lock = new object();

        private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize);
        private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize);

        public SslStream.JavaProxy SslStreamProxy { get; }

        public SafeSslHandle SslContext => _sslContext;

        private volatile bool _disposed;

        public SafeDeleteSslContext(SslAuthenticationOptions authOptions)
            : base(IntPtr.Zero)
        {
            SslStreamProxy = authOptions.SslStreamProxy
                ?? throw new ArgumentNullException(nameof(authOptions.SslStreamProxy));

            try
            {
                _sslContext = CreateSslContext(SslStreamProxy, authOptions);
                _gcHandle = GCHandle.Alloc(this);
                InitializeSslContext(_sslContext, authOptions);
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }

        public override bool IsInvalid => _sslContext?.IsInvalid ?? true;

        protected override void Dispose(bool disposing)
        {
            if (disposing && !_disposed)
            {
                _disposed = true;

                lock (_lock)
                {
                    _inputBuffer.Dispose();
                    _outputBuffer.Dispose();
                    _sslContext.Dispose();
                }


Review comment (suggestion):
This will root the instance until `Dispose` is explicitly called. In case the user doesn't call it (not unheard of), GC will not release it ever, regardless of finalizer existence or not.

We had a similar problem in Quic with MsQuic and we're now only rooting the object for the limited time when the control is in native code.

Code after:
        private static readonly Lazy<SslProtocols> s_supportedSslProtocols = new Lazy<SslProtocols>(Interop.AndroidCrypto.SSLGetSupportedProtocols);

        private readonly SafeSslHandle _sslContext;

        private readonly Lock _lock = new Lock();

        private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize);
        private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize);

        public SslStream.JavaProxy SslStreamProxy { get; }

        public SafeSslHandle SslContext => _sslContext;

        private volatile bool _disposed;

        public SafeDeleteSslContext(SslAuthenticationOptions authOptions)
            : base(IntPtr.Zero)
        {
            SslStreamProxy = authOptions.SslStreamProxy
                ?? throw new ArgumentNullException(nameof(authOptions.SslStreamProxy));

            try
            {
                _sslContext = CreateSslContext(SslStreamProxy, authOptions);
                InitializeSslContext(_sslContext, authOptions);
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }

        public override bool IsInvalid => _sslContext?.IsInvalid ?? true;

        protected override void Dispose(bool disposing)
        {
            lock (_lock)
            {
                if (!_disposed)
                {
                    _disposed = true;

                    // First dispose the SSL context to trigger native cleanup
                    _sslContext.Dispose();

                    if (disposing)
                    {
                        // Then dispose the buffers
                        _inputBuffer.Dispose();