// PushScope // // Push a transaction scope onto the stack. private void PushScope() { // Fixup the interop mode before we set current. if (!_interopModeSpecified) { // Transaction.InteropMode will take the interop mode on // for the scope in currentScope into account. _interopOption = Transaction.InteropMode(_savedCurrentScope); } // async function yield at await points and main thread can continue execution. We need to make sure the TLS data are restored appropriately. SaveTLSContextData(); if (AsyncFlowEnabled) { // Async Flow is enabled and CallContext will be used for ambient transaction. _threadContextData = CallContextCurrentData.CreateOrGetCurrentData(ContextKey); if (_savedCurrentScope == null && _savedCurrent == null) { // Clear TLS data so that transaction doesn't leak from current thread. ContextData.TLSCurrentData = null; } } else { // Legacy TransactionScope. Use TLS to track ambient transaction context. _threadContextData = ContextData.TLSCurrentData; CallContextCurrentData.ClearCurrentData(ContextKey, false); } // This call needs to be done first SetCurrent(_expectedCurrent); _threadContextData.CurrentScope = this; }
// PopScope // // Pop the current transaction scope off the top of the stack private void PopScope() { bool shouldRestoreContextData = true; // Clear the current TransactionScope CallContext data if (AsyncFlowEnabled) { CallContextCurrentData.ClearCurrentData(ContextKey, true); } if (_scopeThread == Thread.CurrentThread) { // async function yield at await points and main thread can continue execution. We need to make sure the TLS data are restored appropriately. // Restore the TLS only if the thread Ids match. RestoreSavedTLSContextData(); } // Restore threadContextData to parent CallContext or TLS data if (_savedCurrentScope != null) { if (_savedCurrentScope.AsyncFlowEnabled) { _threadContextData = CallContextCurrentData.CreateOrGetCurrentData(_savedCurrentScope.ContextKey); } else { if (_savedCurrentScope._scopeThread != Thread.CurrentThread) { // Clear TLS data so that transaction doesn't leak from current thread. shouldRestoreContextData = false; ContextData.TLSCurrentData = null; } else { _threadContextData = ContextData.TLSCurrentData; } CallContextCurrentData.ClearCurrentData(_savedCurrentScope.ContextKey, false); } } else { // No parent TransactionScope present // Clear any CallContext data CallContextCurrentData.ClearCurrentData(null, false); if (_scopeThread != Thread.CurrentThread) { // Clear TLS data so that transaction doesn't leak from current thread. shouldRestoreContextData = false; ContextData.TLSCurrentData = null; } else { // Restore the current data to TLS. ContextData.TLSCurrentData = _threadContextData; } } // prevent restoring the context in an unexpected thread due to thread switch during TransactionScope's Dispose if (shouldRestoreContextData) { _threadContextData.CurrentScope = _savedCurrentScope; RestoreCurrent(); } }