diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs index b19441281e4ba814405c8edd66dbed382a67e6ad..b4d76d70de0aca38298003984b5c69a36316fdb9 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs @@ -34,9 +34,12 @@ internal static class RemoteHostOptions // this is our timeout on how long we will try keep connecting. so far I saw over 2-3 seconds before connection made // when there are many (over 10+ requests) at the same time. one of reasons of this is we put our service hub process as "Below Normal" priority. // normally response time is within 10s ms. at most 100ms. if priority is changed to "Normal", most of time 10s ms. + // + // also another reason why timeout is so big is that, if user put this computer sleep, then timeout can happen. so for now, until we have + // sleep aware timer, we put very long timeout for request service (https://github.com/dotnet/roslyn/pull/22151) [ExportOption] public static readonly Option RequestServiceTimeoutInMS = new Option( - nameof(InternalFeatureOnOffOptions), nameof(RequestServiceTimeoutInMS), defaultValue: 7 * 24 * 60 * 60 * 1000 /* 7 day */, + nameof(InternalFeatureOnOffOptions), nameof(RequestServiceTimeoutInMS), defaultValue: 7 * 24 * 60 * 60 * 1000 /* 7 days */, storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(RequestServiceTimeoutInMS))); // This options allow users to restart OOP when it is killed by users diff --git a/src/VisualStudio/Core/Next/Remote/ServiceHubRemoteHostClient.cs b/src/VisualStudio/Core/Next/Remote/ServiceHubRemoteHostClient.cs index efa68630b9cf0aa75651b4f9ad921c71412e422a..d732ad469fbd71deb00437407a11cfb5be1f81f4 100644 --- a/src/VisualStudio/Core/Next/Remote/ServiceHubRemoteHostClient.cs +++ b/src/VisualStudio/Core/Next/Remote/ServiceHubRemoteHostClient.cs @@ -176,25 +176,30 @@ private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e) { const int retry_delayInMS = 50; - var start = DateTime.UtcNow; - while (DateTime.UtcNow - start < timeout) + using (var pooledStopwatch = SharedPools.Default().GetPooledObject()) { - cancellationToken.ThrowIfCancellationRequested(); + var watch = pooledStopwatch.Object; + watch.Start(); - try - { - return await funcAsync().ConfigureAwait(false); - } - catch (TException) + while (watch.Elapsed < timeout) { - // throw cancellation token if operation is cancelled cancellationToken.ThrowIfCancellationRequested(); - } - // wait for retry_delayInMS before next try - await Task.Delay(retry_delayInMS, cancellationToken).ConfigureAwait(false); + try + { + return await funcAsync().ConfigureAwait(false); + } + catch (TException) + { + // throw cancellation token if operation is cancelled + cancellationToken.ThrowIfCancellationRequested(); + } + + // wait for retry_delayInMS before next try + await Task.Delay(retry_delayInMS, cancellationToken).ConfigureAwait(false); - ReportTimeout(start); + ReportTimeout(watch); + } } // operation timed out, more than we are willing to wait @@ -269,6 +274,7 @@ private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e) throw ExceptionUtilities.Unreachable; } + #region code related to make diagnosis easier later private static int ReportDetailInfo(IFaultUtility faultUtility) { // 0 means send watson, otherwise, cancel watson @@ -328,14 +334,14 @@ private static bool ReportNonIOException(Exception ex) private static readonly TimeSpan s_reportTimeout = TimeSpan.FromMinutes(10); private static bool s_timeoutReported = false; - private static void ReportTimeout(DateTime start) + private static void ReportTimeout(Stopwatch watch) { - // if we tried for 10 min and still couldn't connect. NFW some data - if (!s_timeoutReported && (DateTime.UtcNow - start) > s_reportTimeout) + // if we tried for 10 min and still couldn't connect. NFW (non fatal watson) some data + if (!s_timeoutReported && watch.Elapsed > s_reportTimeout) { s_timeoutReported = true; - // report service hug logs along with dump + // report service hub logs along with dump WatsonReporter.Report("RequestServiceAsync Timeout", new Exception("RequestServiceAsync Timeout"), ReportDetailInfo); } } @@ -355,5 +361,6 @@ private static void ShowInfoBar() ServicesVSResources.Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio); } } + #endregion } } diff --git a/src/Workspaces/Core/Portable/Utilities/ObjectPools/Extensions.cs b/src/Workspaces/Core/Portable/Utilities/ObjectPools/Extensions.cs index 7893aeb81e9f990e1dba5a0e36bd36422d373290..1e7833512a8faece622e211541b4df971ab779b4 100644 --- a/src/Workspaces/Core/Portable/Utilities/ObjectPools/Extensions.cs +++ b/src/Workspaces/Core/Portable/Utilities/ObjectPools/Extensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Diagnostics; using System.Text; namespace Roslyn.Utilities @@ -14,6 +15,11 @@ public static PooledObject GetPooledObject(this ObjectPool.Create(pool); } + public static PooledObject GetPooledObject(this ObjectPool pool) + { + return PooledObject.Create(pool); + } + public static PooledObject> GetPooledObject(this ObjectPool> pool) { return PooledObject>.Create(pool); @@ -52,6 +58,14 @@ public static StringBuilder AllocateAndClear(this ObjectPool pool return sb; } + public static Stopwatch AllocateAndClear(this ObjectPool pool) + { + var watch = pool.Allocate(); + watch.Reset(); + + return watch; + } + public static Stack AllocateAndClear(this ObjectPool> pool) { var set = pool.Allocate(); @@ -109,6 +123,17 @@ public static void ClearAndFree(this ObjectPool pool, StringBuild pool.Free(sb); } + public static void ClearAndFree(this ObjectPool pool, Stopwatch watch) + { + if (watch == null) + { + return; + } + + watch.Reset(); + pool.Free(watch); + } + public static void ClearAndFree(this ObjectPool> pool, HashSet set) { if (set == null) diff --git a/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs b/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs index f0e5d5bd071cf85e662697bd1c86199e57a97a2c..38f2b9c3106f5f14d5ee38128e10f53a4bb25c70 100644 --- a/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs +++ b/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; namespace Roslyn.Utilities @@ -40,6 +41,14 @@ public static PooledObject Create(ObjectPool pool) pool, p => Allocator(p), (p, sb) => Releaser(p, sb)); + } + + public static PooledObject Create(ObjectPool pool) + { + return new PooledObject( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) @@ -94,6 +103,16 @@ private static void Releaser(ObjectPool pool, StringBuilder sb) pool.ClearAndFree(sb); } + private static Stopwatch Allocator(ObjectPool pool) + { + return pool.AllocateAndClear(); + } + + private static void Releaser(ObjectPool pool, Stopwatch sb) + { + pool.ClearAndFree(sb); + } + private static Stack Allocator(ObjectPool> pool) { return pool.AllocateAndClear();