MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Threading;6 7using Dbg = System.Management.Automation.Diagnostics;8 9namespace System.Management.Automation.Remoting10{11 /// <summary>12 /// The ServerDispatchTable class.13 /// </summary>14 internal class ServerDispatchTable : DispatchTable<RemoteHostResponse>15 {16 // DispatchTable specialized for RemoteHostResponse.17 }18 19 /// <summary>20 /// Provides a thread-safe dictionary that maps call-ids to AsyncData objects.21 /// When a thread tries to do a get on a hashtable key (callId) that has not been22 /// set it is blocked. Once the key's value is set the thread is released. This is23 /// used to synchronize server calls with their responses.24 ///25 /// This code needs to be thread-safe. The locking convention is that only the26 /// internal or public methods use locks and are thread-safe. The private methods27 /// do not use locks and are not thread-safe (unless called by the internal and28 /// public methods). If the private methods becomes internal or public29 /// please review the locking.30 /// </summary>31 internal class DispatchTable<T> where T : class32 {33 /// <summary>34 /// Response async objects.35 /// </summary>36 private readonly Dictionary<long, AsyncObject<T>> _responseAsyncObjects = new Dictionary<long, AsyncObject<T>>();37 38 /// <summary>39 /// Next call id.40 /// </summary>41 private long _nextCallId = 0;42 43 /// <summary>44 /// Void call id.45 /// </summary>46 internal const long VoidCallId = -100;47 48 /// <summary>49 /// Create new call id.50 /// </summary>51 internal long CreateNewCallId()52 {53 // Note: Only CreateNewCallId adds new records.54 55 long callId = Interlocked.Increment(ref _nextCallId);56 AsyncObject<T> responseAsyncObject = new AsyncObject<T>();57 lock (_responseAsyncObjects)58 {59 _responseAsyncObjects[callId] = responseAsyncObject;60 }61 62 return callId;63 }64 65 /// <summary>66 /// Get response async object.67 /// </summary>68 private AsyncObject<T> GetResponseAsyncObject(long callId)69 {70 AsyncObject<T> responseAsyncObject = null;71 Dbg.Assert(_responseAsyncObjects.ContainsKey(callId), "Expected _responseAsyncObjects.ContainsKey(callId)");72 responseAsyncObject = _responseAsyncObjects[callId];73 Dbg.Assert(responseAsyncObject != null, "Expected responseAsyncObject != null");74 return responseAsyncObject;75 }76 77 /// <summary>78 /// Waits for response PSObject to be set and then returns it. Returns null79 /// if wait was aborted.80 /// </summary>81 /// <param name="callId">82 /// </param>83 /// <param name="defaultValue">84 /// default return value (in case the remote end did not send response).85 /// </param>86 internal T GetResponse(long callId, T defaultValue)87 {88 // Note: Only GetResponse removes records.89 90 AsyncObject<T> responseAsyncObject = null;91 lock (_responseAsyncObjects)92 {93 responseAsyncObject = GetResponseAsyncObject(callId);94 }95 96 // This will block until Value is set on this AsyncObject.97 T remoteHostResponse = responseAsyncObject.Value;98 99 // Remove table entry to conserve memory: this table could be alive for a long time.100 lock (_responseAsyncObjects)101 {102 _responseAsyncObjects.Remove(callId);103 }104 105 // return caller specified value in case there is no response106 // from remote end.107 if (remoteHostResponse == null)108 {109 return defaultValue;110 }111 112 return remoteHostResponse;113 }114 115 /// <summary>116 /// Set response.117 /// </summary>118 internal void SetResponse(long callId, T remoteHostResponse)119 {120 Dbg.Assert(remoteHostResponse != null, "Expected remoteHostResponse != null");121 lock (_responseAsyncObjects)122 {123 // The response-async-object might not exist if the call was aborted by Ctrl-C or if124 // the call had a void return and no return value was expected.125 if (!_responseAsyncObjects.ContainsKey(callId))126 {127 return;128 }129 130 // Unblock the AsyncObject by setting its value.131 AsyncObject<T> responseAsyncObject = GetResponseAsyncObject(callId);132 responseAsyncObject.Value = remoteHostResponse;133 }134 }135 136 /// <summary>137 /// Abort call.138 /// </summary>139 private void AbortCall(long callId)140 {141 // The response-async-object might not exist if the call was already aborted.142 if (!_responseAsyncObjects.ContainsKey(callId))143 {144 return;145 }146 147 // Releases blocked thread by setting null as return value, which should be detected by caller of GetResponse.148 AsyncObject<T> responseAsyncObject = GetResponseAsyncObject(callId);149 responseAsyncObject.Value = null;150 }151 152 /// <summary>153 /// Abort calls.154 /// </summary>155 private void AbortCalls(List<long> callIds)156 {157 // Releases blocked thread by setting null as return value, which should be detected by caller of GetResponse.158 foreach (long callId in callIds)159 {160 AbortCall(callId);161 }162 }163 164 /// <summary>165 /// Get all calls.166 /// </summary>167 private List<long> GetAllCalls()168 {169 // Gets all the callIds that are waiting on calls.170 List<long> callIds = new List<long>();171 foreach (KeyValuePair<long, AsyncObject<T>> callIdResponseAsyncObjectPair in _responseAsyncObjects)172 {173 callIds.Add(callIdResponseAsyncObjectPair.Key);174 }175 176 return callIds;177 }178 179 /// <summary>180 /// Abort all calls.181 /// </summary>182 internal void AbortAllCalls()183 {184 lock (_responseAsyncObjects)185 {186 List<long> callIds = GetAllCalls();187 AbortCalls(callIds);188 }189 }190 }191}192 