MegaBites-AI/Windows-powershell
0372
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 