Example #1
0
        private static CancellationTokenHelper GetStaticDisposedHelper()
        {
            CancellationTokenHelper helper = new CancellationTokenHelper(false);

            helper.Dispose();
            return(helper);
        }
Example #2
0
 private static CancellationTokenHelper GetStaticDisposedHelper() {
     CancellationTokenHelper helper = new CancellationTokenHelper(false);
     helper.Dispose();
     return helper;
 }
        internal bool TryGetClientDisconnectedCancellationToken(out CancellationToken cancellationToken) {
            // This API is not supported on IIS versions prior to IIS 7.5.
            if (HttpRuntime.IISVersion < IIS_VERSION_75) {
                cancellationToken = CancellationToken.None;
                return false;
            }

            // The registration is done inside of the dispose lock because we don't want to
            // allow registration while the native context object is being destroyed. If we
            // didn't have this lock, there is a potential race condition where the
            // registration could take place at the same time as a call to Dispose, at which
            // point we lose our guarantees about the unmanaged code not trying to call back
            // into this AppDomain.
            //
            // If you change the below code, be judicious with how much work is performed
            // within the lock on _disposeLockObj. The p/invoke call that takes place within
            // the lock will itself try to enter a critical section in the class
            // MGD_CONNECTION_STORED_CONTEXT, and this could very easily lead to deadlock
            // scenarios. The particular usage below is safe because the _disposeLockObj
            // lock is always taken before entering the unmanaged critical section, never
            // the other way around. So code that is within the unmanaged critical section
            // is guaranteed to execute quickly without blocking.

            if (_clientDisconnectTokenHelper == null) {
                lock (_disposeLockObj) {
                    if (_clientDisconnectTokenHelper == null) {
                        if (_context == IntPtr.Zero) {
                            // A call to Dispose already took place, so we'll just return a pre-disposed helper.
                            _clientDisconnectTokenHelper = CancellationTokenHelper.StaticDisposed;
                        }
                        else {
                            bool isClientConnected;
                            int hr = IIS.MgdConfigureAsyncDisconnectNotification(_context, true /* fEnabled */, out isClientConnected);
                            Marshal.ThrowExceptionForHR(hr);

                            // Seed the helper's initial state with the known client connection status.
                            // Pay attention to the race condition mentioned in NotifyOfAsyncDisconnect.
                            LazyInitializer.EnsureInitialized(ref _clientDisconnectTokenHelper, () => new CancellationTokenHelper(canceled: !isClientConnected));
                        }
                    }
                }
            }

            cancellationToken = _clientDisconnectTokenHelper.Token;
            return true;
        }