Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#region Using directives5 6using System;7using System.Collections.Concurrent;8using System.Collections.Generic;9using System.Management.Automation;10using System.Threading;11 12#endregion13 14namespace Microsoft.Management.Infrastructure.CimCmdlets15{16    /// <summary>17    /// <para>18    /// Async operation base class, it will issue async operation through19    /// 1...* CimSession object(s), processing the async results, extended20    /// pssemantics operations, and manage the lifecycle of created21    /// CimSession object(s).22    /// </para>23    /// </summary>24    internal abstract class CimAsyncOperation : IDisposable25    {26        #region Constructor27 28        /// <summary>29        /// Initializes a new instance of the <see cref="CimAsyncOperation"/> class.30        /// </summary>31        protected CimAsyncOperation()32        {33            this.moreActionEvent = new ManualResetEventSlim(false);34            this.actionQueue = new ConcurrentQueue<CimBaseAction>();35            this._disposed = 0;36            this.operationCount = 0;37        }38 39        #endregion40 41        #region Event handler42 43        /// <summary>44        /// <para>45        /// Handler used to handle new action event from46        /// <seealso cref="CimSessionProxy"/> object.47        /// </para>48        /// </summary>49        /// <param name="cimSession">50        /// <seealso cref="CimSession"/> object raised the event51        /// </param>52        /// <param name="actionArgs">Event argument.</param>53        protected void NewCmdletActionHandler(object cimSession, CmdletActionEventArgs actionArgs)54        {55            DebugHelper.WriteLogEx("Disposed {0}, action type = {1}", 0, this.Disposed, actionArgs.Action);56 57            if (this.Disposed)58            {59                if (actionArgs.Action is CimSyncAction)60                {61                    // unblock the thread waiting for response62                    (actionArgs.Action as CimSyncAction).OnComplete();63                }64 65                return;66            }67 68            bool isEmpty = this.actionQueue.IsEmpty;69            this.actionQueue.Enqueue(actionArgs.Action);70            if (isEmpty)71            {72                this.moreActionEvent.Set();73            }74        }75 76        /// <summary>77        /// <para>78        /// Handler used to handle new operation event from79        /// <seealso cref="CimSessionProxy"/> object.80        /// </para>81        /// </summary>82        /// <param name="cimSession">83        /// <seealso cref="CimSession"/> object raised the event.84        /// </param>85        /// <param name="actionArgs">Event argument.</param>86        protected void OperationCreatedHandler(object cimSession, OperationEventArgs actionArgs)87        {88            DebugHelper.WriteLogEx();89 90            lock (this.a_lock)91            {92                this.operationCount++;93            }94        }95 96        /// <summary>97        /// <para>98        /// Handler used to handle operation deletion event from99        /// <seealso cref="CimSessionProxy"/> object.100        /// </para>101        /// </summary>102        /// <param name="cimSession">103        /// <seealso cref="CimSession"/> object raised the event.104        /// </param>105        /// <param name="actionArgs">Event argument.</param>106        protected void OperationDeletedHandler(object cimSession, OperationEventArgs actionArgs)107        {108            DebugHelper.WriteLogEx();109 110            lock (this.a_lock)111            {112                this.operationCount--;113                if (this.operationCount == 0)114                {115                    this.moreActionEvent.Set();116                }117            }118        }119 120        #endregion121 122        /// <summary>123        /// <para>124        /// process all actions in the action queue125        /// </para>126        /// </summary>127        /// <param name="cmdletOperation">128        /// Wrapper of cmdlet, <seealso cref="CmdletOperationBase"/> for details.129        /// </param>130        public void ProcessActions(CmdletOperationBase cmdletOperation)131        {132            if (!this.actionQueue.IsEmpty)133            {134                CimBaseAction action;135                while (GetActionAndRemove(out action))136                {137                    action.Execute(cmdletOperation);138                    if (this.Disposed)139                    {140                        break;141                    }142                }143            }144        }145 146        /// <summary>147        /// <para>148        /// Process remaining actions until all operations are completed or149        /// current cmdlet is terminated by user.150        /// </para>151        /// </summary>152        /// <param name="cmdletOperation">153        /// Wrapper of cmdlet, <seealso cref="CmdletOperationBase"/> for details.154        /// </param>155        public void ProcessRemainActions(CmdletOperationBase cmdletOperation)156        {157            DebugHelper.WriteLogEx();158 159            while (true)160            {161                ProcessActions(cmdletOperation);162                if (!this.IsActive())163                {164                    DebugHelper.WriteLogEx("Either disposed or all operations completed.", 2);165                    break;166                }167 168                try169                {170                    this.moreActionEvent.Wait();171                    this.moreActionEvent.Reset();172                }173                catch (ObjectDisposedException ex)174                {175                    // This might happen if this object is being disposed,176                    // while another thread is processing the remaining actions177                    DebugHelper.WriteLogEx("moreActionEvent was disposed: {0}.", 2, ex);178                    break;179                }180            }181 182            ProcessActions(cmdletOperation);183        }184 185        #region helper methods186 187        /// <summary>188        /// <para>189        /// Get action object from action queue.190        /// </para>191        /// </summary>192        /// <param name="action">Next action to execute.</param>193        /// <returns>True indicates there is an valid action, otherwise false.</returns>194        protected bool GetActionAndRemove(out CimBaseAction action)195        {196            return this.actionQueue.TryDequeue(out action);197        }198 199        /// <summary>200        /// <para>201        /// Add temporary <seealso cref="CimSessionProxy"/> object to cache.202        /// </para>203        /// </summary>204        /// <param name="sessionproxy">Cimsession wrapper object.</param>205        protected void AddCimSessionProxy(CimSessionProxy sessionproxy)206        {207            lock (cimSessionProxyCacheLock)208            {209                this.cimSessionProxyCache ??= new List<CimSessionProxy>();210 211                if (!this.cimSessionProxyCache.Contains(sessionproxy))212                {213                    this.cimSessionProxyCache.Add(sessionproxy);214                }215            }216        }217 218        /// <summary>219        /// <para>220        /// Are there active operations?221        /// </para>222        /// </summary>223        /// <returns>True for having active operations, otherwise false.</returns>224        protected bool IsActive()225        {226            DebugHelper.WriteLogEx("Disposed {0}, Operation Count {1}", 2, this.Disposed, this.operationCount);227            bool isActive = (!this.Disposed) && (this.operationCount > 0);228            return isActive;229        }230 231        /// <summary>232        /// Create <see cref="CimSessionProxy"/> object.233        /// </summary>234        /// <param name="session"></param>235        protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy)236        {237            CimSessionProxy proxy = new(originalProxy);238            this.SubscribeEventAndAddProxytoCache(proxy);239            return proxy;240        }241 242        /// <summary>243        /// Create <see cref="CimSessionProxy"/> object.244        /// </summary>245        /// <param name="session"></param>246        protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy, bool passThru)247        {248            CimSessionProxy proxy = new CimSessionProxySetCimInstance(originalProxy, passThru);249            this.SubscribeEventAndAddProxytoCache(proxy);250            return proxy;251        }252 253        /// <summary>254        /// Create <see cref="CimSessionProxy"/> object.255        /// </summary>256        /// <param name="session"></param>257        protected CimSessionProxy CreateCimSessionProxy(CimSession session)258        {259            CimSessionProxy proxy = new(session);260            this.SubscribeEventAndAddProxytoCache(proxy);261            return proxy;262        }263 264        /// <summary>265        /// Create <see cref="CimSessionProxy"/> object.266        /// </summary>267        /// <param name="session"></param>268        protected CimSessionProxy CreateCimSessionProxy(CimSession session, bool passThru)269        {270            CimSessionProxy proxy = new CimSessionProxySetCimInstance(session, passThru);271            this.SubscribeEventAndAddProxytoCache(proxy);272            return proxy;273        }274 275        /// <summary>276        /// Create <see cref="CimSessionProxy"/> object, and277        /// add the proxy into cache.278        /// </summary>279        /// <param name="computerName"></param>280        protected CimSessionProxy CreateCimSessionProxy(string computerName)281        {282            CimSessionProxy proxy = new(computerName);283            this.SubscribeEventAndAddProxytoCache(proxy);284            return proxy;285        }286 287        /// <summary>288        /// Create <see cref="CimSessionProxy"/> object, and289        /// add the proxy into cache.290        /// </summary>291        /// <param name="computerName"></param>292        /// <param name="cimInstance"></param>293        /// <returns></returns>294        protected CimSessionProxy CreateCimSessionProxy(string computerName, CimInstance cimInstance)295        {296            CimSessionProxy proxy = new(computerName, cimInstance);297            this.SubscribeEventAndAddProxytoCache(proxy);298            return proxy;299        }300 301        /// <summary>302        /// Create <see cref="CimSessionProxy"/> object, and303        /// add the proxy into cache.304        /// </summary>305        /// <param name="computerName"></param>306        /// <param name="cimInstance"></param>307        /// <param name="passThru"></param>308        protected CimSessionProxy CreateCimSessionProxy(string computerName, CimInstance cimInstance, bool passThru)309        {310            CimSessionProxy proxy = new CimSessionProxySetCimInstance(computerName, cimInstance, passThru);311            this.SubscribeEventAndAddProxytoCache(proxy);312            return proxy;313        }314 315        /// <summary>316        /// Subscribe event from proxy and add proxy to cache.317        /// </summary>318        /// <param name="proxy"></param>319        protected void SubscribeEventAndAddProxytoCache(CimSessionProxy proxy)320        {321            this.AddCimSessionProxy(proxy);322            SubscribeToCimSessionProxyEvent(proxy);323        }324 325        /// <summary>326        /// <para>327        /// Subscribe to the events issued by <see cref="CimSessionProxy"/>.328        /// </para>329        /// </summary>330        /// <param name="proxy"></param>331        protected virtual void SubscribeToCimSessionProxyEvent(CimSessionProxy proxy)332        {333            DebugHelper.WriteLogEx();334 335            proxy.OnNewCmdletAction += this.NewCmdletActionHandler;336            proxy.OnOperationCreated += this.OperationCreatedHandler;337            proxy.OnOperationDeleted += this.OperationDeletedHandler;338        }339 340        /// <summary>341        /// Retrieve the base object out if wrapped in psobject.342        /// </summary>343        /// <param name="value"></param>344        /// <returns></returns>345        protected object GetBaseObject(object value)346        {347            if (value is not PSObject psObject)348            {349                return value;350            }351            else352            {353                object baseObject = psObject.BaseObject;354                if (baseObject is not object[] arrayObject)355                {356                    return baseObject;357                }358                else359                {360                    object[] arraybaseObject = new object[arrayObject.Length];361                    for (int i = 0; i < arrayObject.Length; i++)362                    {363                        arraybaseObject[i] = GetBaseObject(arrayObject[i]);364                    }365 366                    return arraybaseObject;367                }368            }369        }370 371        /// <summary>372        /// Retrieve the reference object or reference array object.373        /// The returned object has to be either CimInstance or CImInstance[] type,374        /// if not thrown exception.375        /// </summary>376        /// <param name="value"></param>377        /// <param name="referenceType">Output the cimtype of the value, either Reference or ReferenceArray.</param>378        /// <returns>The object.</returns>379        protected object GetReferenceOrReferenceArrayObject(object value, ref CimType referenceType)380        {381            if (value is PSReference cimReference)382            {383                object baseObject = GetBaseObject(cimReference.Value);384                if (baseObject is not CimInstance cimInstance)385                {386                    return null;387                }388 389                referenceType = CimType.Reference;390                return cimInstance;391            }392            else393            {394                if (value is not object[] cimReferenceArray)395                {396                    return null;397                }398                else if (cimReferenceArray[0] is not PSReference)399                {400                    return null;401                }402 403                CimInstance[] cimInstanceArray = new CimInstance[cimReferenceArray.Length];404                for (int i = 0; i < cimReferenceArray.Length; i++)405                {406                    if (cimReferenceArray[i] is not PSReference tempCimReference)407                    {408                        return null;409                    }410 411                    object baseObject = GetBaseObject(tempCimReference.Value);412                    cimInstanceArray[i] = baseObject as CimInstance;413                    if (cimInstanceArray[i] == null)414                    {415                        return null;416                    }417                }418 419                referenceType = CimType.ReferenceArray;420                return cimInstanceArray;421            }422        }423        #endregion424 425        #region IDisposable426 427        /// <summary>428        /// <para>429        /// Indicates whether this object was disposed or not430        /// </para>431        /// </summary>432        protected bool Disposed433        {434            get435            {436                return this._disposed == 1;437            }438        }439 440        private int _disposed;441 442        /// <summary>443        /// <para>444        /// Dispose() calls Dispose(true).445        /// Implement IDisposable. Do not make this method virtual.446        /// A derived class should not be able to override this method.447        /// </para>448        /// </summary>449        public void Dispose()450        {451            Dispose(true);452 453            // This object will be cleaned up by the Dispose method.454            // Therefore, you should call GC.SuppressFinalize to455            // take this object off the finalization queue456            // and prevent finalization code for this object457            // from executing a second time.458            GC.SuppressFinalize(this);459        }460 461        /// <summary>462        /// <para>463        /// Dispose(bool disposing) executes in two distinct scenarios.464        /// If disposing equals true, the method has been called directly465        /// or indirectly by a user's code. Managed and unmanaged resources466        /// can be disposed.467        /// If disposing equals false, the method has been called by the468        /// runtime from inside the finalizer and you should not reference469        /// other objects. Only unmanaged resources can be disposed.470        /// </para>471        /// </summary>472        /// <param name="disposing">Whether it is directly called.</param>473        protected virtual void Dispose(bool disposing)474        {475            if (Interlocked.CompareExchange(ref this._disposed, 1, 0) == 0)476            {477                if (disposing)478                {479                    // free managed resources480                    Cleanup();481                }482                // free native resources if there are any483            }484        }485 486        /// <summary>487        /// <para>488        /// Clean up managed resources.489        /// </para>490        /// </summary>491        private void Cleanup()492        {493            DebugHelper.WriteLogEx();494 495            // unblock thread that waiting for more actions496            this.moreActionEvent.Set();497            CimBaseAction action;498            while (GetActionAndRemove(out action))499            {500                DebugHelper.WriteLog("Action {0}", 2, action);501 502                if (action is CimSyncAction)503                {504                    // unblock the thread waiting for response505                    (action as CimSyncAction).OnComplete();506                }507            }508 509            if (this.cimSessionProxyCache != null)510            {511                List<CimSessionProxy> temporaryProxy;512                lock (this.cimSessionProxyCache)513                {514                    temporaryProxy = new List<CimSessionProxy>(this.cimSessionProxyCache);515                    this.cimSessionProxyCache.Clear();516                }517 518                // clean up all proxy objects519                foreach (CimSessionProxy proxy in temporaryProxy)520                {521                    DebugHelper.WriteLog("Dispose proxy ", 2);522                    proxy.Dispose();523                }524            }525 526            this.moreActionEvent.Dispose();527            this.ackedEvent?.Dispose();528 529            DebugHelper.WriteLog("Cleanup complete.", 2);530        }531 532        #endregion533 534        #region private members535 536        /// <summary>537        /// Lock object.538        /// </summary>539        private readonly object a_lock = new();540 541        /// <summary>542        /// Number of active operations.543        /// </summary>544        private uint operationCount;545 546        /// <summary>547        /// Event to notify ps thread that more action is available.548        /// </summary>549        private readonly ManualResetEventSlim moreActionEvent;550 551        /// <summary>552        /// The following is the definition of action queue.553        /// The queue holding all actions to be executed in the context of either554        /// ProcessRecord or EndProcessing.555        /// </summary>556        private readonly ConcurrentQueue<CimBaseAction> actionQueue;557 558        /// <summary>559        /// Lock object.560        /// </summary>561        private readonly object cimSessionProxyCacheLock = new();562 563        /// <summary>564        /// Cache all <see cref="CimSessionProxy"/> objects related to565        /// the current operation.566        /// </summary>567        private List<CimSessionProxy> cimSessionProxyCache;568 569        #endregion570 571        #region protected members572        /// <summary>573        /// Event to notify ps thread that either a ACK message sent back574        /// or a error happened. Currently only used by575        /// <see cref="CimRegisterCimIndication"/>.576        /// </summary>577        protected ManualResetEventSlim ackedEvent;578        #endregion579 580        #region const strings581        internal const string ComputerNameArgument = @"ComputerName";582        internal const string CimSessionArgument = @"CimSession";583        #endregion584    }585}586