Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
TransactionManager.cs709 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915 6using System.Collections.Generic;7using System.Transactions;8using System.Management.Automation.Internal;9 10namespace System.Management.Automation11{12    /// <summary>13    /// The status of a PowerShell transaction.14    /// </summary>15    public enum PSTransactionStatus16    {17        /// <summary>18        /// The transaction has been rolled back.19        /// </summary>20        RolledBack = 0,21 22        /// <summary>23        /// The transaction has been committed.24        /// </summary>25        Committed = 1,26 27        /// <summary>28        /// The transaction is currently active.29        /// </summary>30        Active = 231    }32 33    /// <summary>34    /// Represents an active transaction.35    /// </summary>36    public sealed class PSTransaction : IDisposable37    {38        /// <summary>39        /// Initializes a new instance of the PSTransaction class.40        /// </summary>41        internal PSTransaction(RollbackSeverity rollbackPreference, TimeSpan timeout)42        {43            _transaction = new CommittableTransaction(timeout);44            RollbackPreference = rollbackPreference;45            _subscriberCount = 1;46        }47 48        /// <summary>49        /// Initializes a new instance of the PSTransaction class using a CommittableTransaction.50        /// </summary>51        internal PSTransaction(CommittableTransaction transaction, RollbackSeverity severity)52        {53            _transaction = transaction;54            RollbackPreference = severity;55            _subscriberCount = 1;56        }57 58        private CommittableTransaction _transaction;59 60        /// <summary>61        /// Gets the rollback preference for this transaction.62        /// </summary>63        public RollbackSeverity RollbackPreference { get; }64 65        /// <summary>66        /// Gets the number of subscribers to this transaction.67        /// </summary>68        public int SubscriberCount69        {70            get71            {72                // Verify the transaction hasn't been rolled back beneath us73                if (this.IsRolledBack)74                {75                    this.SubscriberCount = 0;76                }77 78                return _subscriberCount;79            }80 81            set { _subscriberCount = value; }82        }83 84        private int _subscriberCount;85 86        /// <summary>87        /// Returns the status of this transaction.88        /// </summary>89        public PSTransactionStatus Status90        {91            get92            {93                if (IsRolledBack)94                {95                    return PSTransactionStatus.RolledBack;96                }97                else if (IsCommitted)98                {99                    return PSTransactionStatus.Committed;100                }101                else102                {103                    return PSTransactionStatus.Active;104                }105            }106        }107 108        /// <summary>109        /// Activates the transaction held by this PSTransaction.110        /// </summary>111        internal void Activate()112        {113            Transaction.Current = _transaction;114        }115 116        /// <summary>117        /// Commits the transaction held by this PSTransaction.118        /// </summary>119        internal void Commit()120        {121            _transaction.Commit();122            IsCommitted = true;123        }124 125        /// <summary>126        /// Rolls back the transaction held by this PSTransaction.127        /// </summary>128        internal void Rollback()129        {130            _transaction.Rollback();131            _isRolledBack = true;132        }133 134        /// <summary>135        /// Determines whether this PSTransaction has been136        /// rolled back or not.137        /// </summary>138        internal bool IsRolledBack139        {140            get141            {142                // Check if it's been aborted underneath us143                if (144                    (!_isRolledBack) &&145                    (_transaction != null) &&146                    (_transaction.TransactionInformation.Status == TransactionStatus.Aborted))147                {148                    _isRolledBack = true;149                }150 151                return _isRolledBack;152            }153 154            set155            {156                _isRolledBack = value;157            }158        }159 160        private bool _isRolledBack = false;161 162        /// <summary>163        /// Determines whether this PSTransaction164        /// has been committed or not.165        /// </summary>166        internal bool IsCommitted { get; set; } = false;167 168        /// <summary>169        /// Destructor for the PSTransaction class.170        /// </summary>171        ~PSTransaction()172        {173            Dispose(false);174        }175 176        /// <summary>177        /// Disposes the PSTransaction object.178        /// </summary>179        public void Dispose()180        {181            Dispose(true);182            GC.SuppressFinalize(this);183        }184 185        /// <summary>186        /// Disposes the PSTransaction object, which disposes the187        /// underlying transaction.188        /// </summary>189        /// <param name="disposing">190        /// Whether to actually dispose the object.191        /// </param>192        public void Dispose(bool disposing)193        {194            if (disposing)195            {196                if (_transaction != null)197                {198                    _transaction.Dispose();199                }200            }201        }202    }203 204    /// <summary>205    /// Supports the transaction management infrastructure for the PowerShell engine.206    /// </summary>207    public sealed class PSTransactionContext : IDisposable208    {209        /// <summary>210        /// Initializes a new instance of the PSTransactionManager class.211        /// </summary>212        internal PSTransactionContext(PSTransactionManager transactionManager)213        {214            _transactionManager = transactionManager;215            transactionManager.SetActive();216        }217 218        private PSTransactionManager _transactionManager;219 220        /// <summary>221        /// Destructor for the PSTransactionManager class.222        /// </summary>223        ~PSTransactionContext()224        {225            Dispose(false);226        }227 228        /// <summary>229        /// Disposes the PSTransactionContext object.230        /// </summary>231        public void Dispose()232        {233            Dispose(true);234            GC.SuppressFinalize(this);235        }236 237        /// <summary>238        /// Disposes the PSTransactionContext object, which resets the239        /// active PSTransaction.240        /// </summary>241        /// <param name="disposing">242        /// Whether to actually dispose the object.243        /// </param>244        private void Dispose(bool disposing)245        {246            if (disposing)247            {248                _transactionManager.ResetActive();249            }250        }251    }252 253    /// <summary>254    /// The severity of error that causes PowerShell to automatically255    /// rollback the transaction.256    /// </summary>257    public enum RollbackSeverity258    {259        /// <summary>260        /// Non-terminating errors or worse.261        /// </summary>262        Error,263 264        /// <summary>265        /// Terminating errors or worse.266        /// </summary>267        TerminatingError,268 269        /// <summary>270        /// Do not rollback the transaction on error.271        /// </summary>272        Never273    }274}275 276namespace System.Management.Automation.Internal277{278    /// <summary>279    /// Supports the transaction management infrastructure for the PowerShell engine.280    /// </summary>281    internal sealed class PSTransactionManager : IDisposable282    {283        /// <summary>284        /// Initializes a new instance of the PSTransactionManager class.285        /// </summary>286        internal PSTransactionManager()287        {288            _transactionStack = new Stack<PSTransaction>();289            _transactionStack.Push(null);290        }291 292        /// <summary>293        /// Called by engine APIs to ensure they are protected from294        /// ambient transactions.295        /// </summary>296        internal static IDisposable GetEngineProtectionScope()297        {298            if (s_engineProtectionEnabled && (Transaction.Current != null))299            {300                return new System.Transactions.TransactionScope(301                    System.Transactions.TransactionScopeOption.Suppress);302            }303            else304            {305                return null;306            }307        }308 309        /// <summary>310        /// Called by the transaction manager to enable engine311        /// protection the first time a transaction is activated.312        /// Engine protection APIs remain protected from this point on.313        /// </summary>314        internal static void EnableEngineProtection()315        {316            s_engineProtectionEnabled = true;317        }318 319        private static bool s_engineProtectionEnabled = false;320 321        /// <summary>322        /// Gets the rollback preference for the active transaction.323        /// </summary>324        internal RollbackSeverity RollbackPreference325        {326            get327            {328                PSTransaction currentTransaction = _transactionStack.Peek();329 330                if (currentTransaction == null)331                {332                    string error = TransactionStrings.NoTransactionActive;333 334                    // This is not an expected condition, and is just protective335                    // coding.336#pragma warning suppress 56503337                    throw new InvalidOperationException(error);338                }339 340                return currentTransaction.RollbackPreference;341            }342        }343 344        /// <summary>345        /// Creates a new Transaction if none are active. Otherwise, increments346        /// the subscriber count for the active transaction.347        /// </summary>348        internal void CreateOrJoin()349        {350            CreateOrJoin(RollbackSeverity.Error, TimeSpan.FromMinutes(1));351        }352 353        /// <summary>354        /// Creates a new Transaction if none are active. Otherwise, increments355        /// the subscriber count for the active transaction.356        /// </summary>357        internal void CreateOrJoin(RollbackSeverity rollbackPreference, TimeSpan timeout)358        {359            PSTransaction currentTransaction = _transactionStack.Peek();360 361            // There is a transaction on the stack362            if (currentTransaction != null)363            {364                // If you are already in a transaction that has been aborted, or committed,365                // create it.366                if (currentTransaction.IsRolledBack || currentTransaction.IsCommitted)367                {368                    // Clean up the "used" one369                    _transactionStack.Pop().Dispose();370 371                    // And add a new one to the stack372                    _transactionStack.Push(new PSTransaction(rollbackPreference, timeout));373                }374                else375                {376                    // This is a usable one. Add a subscriber to it.377                    currentTransaction.SubscriberCount++;378                }379            }380            else381            {382                // Add a new transaction to the stack383                _transactionStack.Push(new PSTransaction(rollbackPreference, timeout));384            }385        }386 387        /// <summary>388        /// Creates a new Transaction that should be managed independently of389        /// any parent transactions.390        /// </summary>391        internal void CreateNew()392        {393            CreateNew(RollbackSeverity.Error, TimeSpan.FromMinutes(1));394        }395 396        /// <summary>397        /// Creates a new Transaction that should be managed independently of398        /// any parent transactions.399        /// </summary>400        internal void CreateNew(RollbackSeverity rollbackPreference, TimeSpan timeout)401        {402            _transactionStack.Push(new PSTransaction(rollbackPreference, timeout));403        }404 405        /// <summary>406        /// Completes the current transaction. If only one subscriber is active, this407        /// commits the transaction. Otherwise, it reduces the subscriber count by one.408        /// </summary>409        internal void Commit()410        {411            PSTransaction currentTransaction = _transactionStack.Peek();412 413            // Should not be able to commit a transaction that is not active414            if (currentTransaction == null)415            {416                string error = TransactionStrings.NoTransactionActiveForCommit;417                throw new InvalidOperationException(error);418            }419 420            // If you are already in a transaction that has been aborted421            if (currentTransaction.IsRolledBack)422            {423                string error = TransactionStrings.TransactionRolledBackForCommit;424                throw new TransactionAbortedException(error);425            }426 427            // If you are already in a transaction that has been committed428            if (currentTransaction.IsCommitted)429            {430                string error = TransactionStrings.CommittedTransactionForCommit;431                throw new InvalidOperationException(error);432            }433 434            if (currentTransaction.SubscriberCount == 1)435            {436                currentTransaction.Commit();437                currentTransaction.SubscriberCount = 0;438            }439            else440            {441                currentTransaction.SubscriberCount--;442            }443 444            // Now that we've committed, go back to the last available transaction445            while ((_transactionStack.Count > 2) &&446                (_transactionStack.Peek().IsRolledBack || _transactionStack.Peek().IsCommitted))447            {448                _transactionStack.Pop().Dispose();449            }450        }451 452        /// <summary>453        /// Aborts the current transaction, no matter how many subscribers are part of it.454        /// </summary>455        internal void Rollback()456        {457            Rollback(false);458        }459 460        /// <summary>461        /// Aborts the current transaction, no matter how many subscribers are part of it.462        /// </summary>463        internal void Rollback(bool suppressErrors)464        {465            PSTransaction currentTransaction = _transactionStack.Peek();466 467            // Should not be able to roll back a transaction that is not active468            if (currentTransaction == null)469            {470                string error = TransactionStrings.NoTransactionActiveForRollback;471                throw new InvalidOperationException(error);472            }473 474            // If you are already in a transaction that has been aborted475            if (currentTransaction.IsRolledBack)476            {477                if (!suppressErrors)478                {479                    // Otherwise, you should not be able to roll it back.480                    string error = TransactionStrings.TransactionRolledBackForRollback;481                    throw new TransactionAbortedException(error);482                }483            }484 485            // See if they've already committed the transaction486            if (currentTransaction.IsCommitted)487            {488                if (!suppressErrors)489                {490                    string error = TransactionStrings.CommittedTransactionForRollback;491                    throw new InvalidOperationException(error);492                }493            }494 495            // Roll back the transaction if it hasn't been rolled back496            currentTransaction.SubscriberCount = 0;497            currentTransaction.Rollback();498 499            // Now that we've rolled back, go back to the last available transaction500            while ((_transactionStack.Count > 2) &&501                (_transactionStack.Peek().IsRolledBack || _transactionStack.Peek().IsCommitted))502            {503                _transactionStack.Pop().Dispose();504            }505        }506 507        /// <summary>508        /// Sets the base transaction; any transactions created thereafter will be nested to this instance.509        /// </summary>510        internal void SetBaseTransaction(CommittableTransaction transaction, RollbackSeverity severity)511        {512            if (this.HasTransaction)513            {514                throw new InvalidOperationException(TransactionStrings.BaseTransactionMustBeFirst);515            }516 517            PSTransaction currentTransaction = _transactionStack.Peek();518 519            // If there is a "used" transaction at the top of the stack, clean it up520            while (_transactionStack.Peek() != null &&521                (_transactionStack.Peek().IsRolledBack || _transactionStack.Peek().IsCommitted))522            {523                _transactionStack.Pop().Dispose();524            }525 526            _baseTransaction = new PSTransaction(transaction, severity);527            _transactionStack.Push(_baseTransaction);528        }529 530        /// <summary>531        /// Removes the transaction added by SetBaseTransaction.532        /// </summary>533        internal void ClearBaseTransaction()534        {535            if (_baseTransaction == null)536            {537                throw new InvalidOperationException(TransactionStrings.BaseTransactionNotSet);538            }539 540            if (_transactionStack.Peek() != _baseTransaction)541            {542                throw new InvalidOperationException(TransactionStrings.BaseTransactionNotActive);543            }544 545            _transactionStack.Pop().Dispose();546            _baseTransaction = null;547        }548 549        private Stack<PSTransaction> _transactionStack;550        private PSTransaction _baseTransaction;551 552        /// <summary>553        /// Returns the current engine transaction.554        /// </summary>555        internal PSTransaction GetCurrent()556        {557            return _transactionStack.Peek();558        }559 560        /// <summary>561        /// Activates the current transaction, both in the engine, and in the Ambient.562        /// </summary>563        internal void SetActive()564        {565            PSTransactionManager.EnableEngineProtection();566 567            PSTransaction currentTransaction = _transactionStack.Peek();568 569            // Should not be able to activate a transaction that is not active570            if (currentTransaction == null)571            {572                string error = TransactionStrings.NoTransactionForActivation;573                throw new InvalidOperationException(error);574            }575 576            // If you are already in a transaction that has been aborted, you should577            // not be able to activate it.578            if (currentTransaction.IsRolledBack)579            {580                string error = TransactionStrings.NoTransactionForActivationBecauseRollback;581                throw new TransactionAbortedException(error);582            }583 584            _previousActiveTransaction = Transaction.Current;585            currentTransaction.Activate();586        }587 588        private Transaction _previousActiveTransaction;589 590        /// <summary>591        /// Deactivates the current transaction in the engine, and restores the592        /// ambient transaction.593        /// </summary>594        internal void ResetActive()595        {596            // Even if you are in a transaction that has been aborted, you597            // should still be able to restore the current transaction.598 599            Transaction.Current = _previousActiveTransaction;600            _previousActiveTransaction = null;601        }602 603        /// <summary>604        /// Determines if you have a transaction that you can set active and work on.605        /// </summary>606        internal bool HasTransaction607        {608            get609            {610                PSTransaction currentTransaction = _transactionStack.Peek();611 612                if ((currentTransaction != null) &&613                    (!currentTransaction.IsCommitted) &&614                    (!currentTransaction.IsRolledBack))615                {616                    return true;617                }618                else619                {620                    return false;621                }622            }623        }624 625        /// <summary>626        /// Determines if the last transaction has been committed.627        /// </summary>628        internal bool IsLastTransactionCommitted629        {630            get631            {632                PSTransaction currentTransaction = _transactionStack.Peek();633 634                if (currentTransaction != null)635                {636                    return currentTransaction.IsCommitted;637                }638                else639                {640                    return false;641                }642            }643        }644 645        /// <summary>646        /// Determines if the last transaction has been rolled back.647        /// </summary>648        internal bool IsLastTransactionRolledBack649        {650            get651            {652                PSTransaction currentTransaction = _transactionStack.Peek();653 654                if (currentTransaction != null)655                {656                    return currentTransaction.IsRolledBack;657                }658                else659                {660                    return false;661                }662            }663        }664 665        /// <summary>666        /// Destructor for the PSTransactionManager class.667        /// </summary>668        ~PSTransactionManager()669        {670            Dispose(false);671        }672 673        /// <summary>674        /// Disposes the PSTransactionManager object.675        /// </summary>676        public void Dispose()677        {678            Dispose(true);679            GC.SuppressFinalize(this);680        }681 682        /// <summary>683        /// Disposes the PSTransactionContext object, which resets the684        /// active PSTransaction.685        /// </summary>686        /// <param name="disposing">687        /// Whether to actually dispose the object.688        /// </param>689        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "baseTransaction", Justification = "baseTransaction should not be disposed since we do not own it - it belongs to the caller")]690        public void Dispose(bool disposing)691        {692            if (disposing)693            {694                ResetActive();695 696                while (_transactionStack.Peek() != null)697                {698                    PSTransaction currentTransaction = _transactionStack.Pop();699 700                    if (currentTransaction != _baseTransaction)701                    {702                        currentTransaction.Dispose();703                    }704                }705            }706        }707    }708}709