diff --git a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs index bf6e0b9e58f0a..bc02f070bd4db 100644 --- a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs @@ -109,7 +109,7 @@ public class ServiceProvider : IServiceProvider public ServiceProvider(bool runCacheCleanup) { _storage = runCacheCleanup ? - new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)) : + new AssetStorage(cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1), gcAfter: TimeSpan.FromMinutes(5)) : new AssetStorage(); } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs index d805f54fd8e44..1e6ff105a93a3 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs @@ -15,8 +15,10 @@ namespace Microsoft.CodeAnalysis.Diagnostics { internal static class DiagnosticResultSerializer { - public static void Serialize(ObjectWriter writer, DiagnosticAnalysisResultMap result, CancellationToken cancellationToken) + public static (int diagnostics, int telemetry, int exceptions) Serialize( + ObjectWriter writer, DiagnosticAnalysisResultMap result, CancellationToken cancellationToken) { + var diagnosticCount = 0; var diagnosticSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); var analysisResult = result.AnalysisResult; @@ -26,11 +28,12 @@ public static void Serialize(ObjectWriter writer, DiagnosticAnalysisResultMap Deserialize( @@ -103,18 +109,24 @@ public static DiagnosticAnalysisResultMap> diagnostics, CancellationToken cancellationToken) { + var count = 0; + writer.WriteInt32(diagnostics.Count); foreach (var kv in diagnostics) { kv.Key.WriteTo(writer); serializer.WriteTo(writer, kv.Value, cancellationToken); + + count += kv.Value.Length; } + + return count; } private static ImmutableDictionary> Deserialize( diff --git a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb index 662f77775a28b..9c724cdfdacbf 100644 --- a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb +++ b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb @@ -70,7 +70,12 @@ Public Class BuildDevDivInsertionFiles "StreamJsonRpc.resources.dll", "codeAnalysisService.servicehub.service.json", "remoteHostService.servicehub.service.json", - "serviceHubSnapshotService.servicehub.service.json", + "snapshotService.servicehub.service.json", + "remoteSymbolSearchUpdateEngine.servicehub.service.json", + "codeAnalysisService64.servicehub.service.json", + "remoteHostService64.servicehub.service.json", + "snapshotService64.servicehub.service.json", + "remoteSymbolSearchUpdateEngine64.servicehub.service.json", "Microsoft.Build.Conversion.Core.dll", "Microsoft.Build.dll", "Microsoft.Build.Engine.dll", @@ -836,8 +841,6 @@ Public Class BuildDevDivInsertionFiles add("Exes\VBCSCompiler\net46\VBCSCompiler.exe.config") add("Exes\InteractiveHost\InteractiveHost.exe.config") add("Exes\csi\csi.rsp") - add("Vsix\Roslyn.Deployment.Full.Next\remoteSymbolSearchUpdateEngine.servicehub.service.json") - add("Vsix\Roslyn.Deployment.Full.Next\snapshotService.servicehub.service.json") add("Vsix\VisualStudioInteractiveComponents\CSharpInteractive.rsp") add("Vsix\VisualStudioSetup\Microsoft.VisualStudio.CallHierarchy.Package.Definitions.dll") add("Vsix\VisualStudioSetup\System.Composition.Convention.dll") diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index dff9fcbfa143c..5aaf1c8b43482 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Execution; +using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; @@ -87,6 +88,9 @@ public void Enable() return; } + // set bitness + SetRemoteHostBitness(); + // make sure we run it on background thread _shutdownCancellationTokenSource = new CancellationTokenSource(); @@ -159,6 +163,23 @@ public Task TryGetRemoteHostClientAsync(CancellationToken canc return remoteClientTask; } + private void SetRemoteHostBitness() + { + var x64 = _workspace.Options.GetOption(RemoteHostOptions.OOP64Bit); + if (!x64) + { + x64 = _workspace.Services.GetService().IsExperimentEnabled( + WellKnownExperimentNames.RoslynOOP64bit); + } + + // log OOP bitness + Logger.Log(FunctionId.RemoteHost_Bitness, KeyValueLogMessage.Create(LogType.Trace, m => m["64bit"] = x64)); + + // set service bitness + WellKnownRemoteHostServices.Set64bit(x64); + WellKnownServiceHubServices.Set64bit(x64); + } + private async Task EnableAsync(CancellationToken cancellationToken) { // if we reached here, IRemoteHostClientFactory must exist. diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs index 978d8a0317bc8..cd034f2291541 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostOptions.cs @@ -44,6 +44,11 @@ internal static class RemoteHostOptions nameof(InternalFeatureOnOffOptions), nameof(RestartRemoteHostAllowed), defaultValue: false, storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(RestartRemoteHostAllowed))); + // use 64bit OOP + public static readonly Option OOP64Bit = new Option( + nameof(InternalFeatureOnOffOptions), nameof(OOP64Bit), defaultValue: false, + storageLocations: new LocalUserProfileStorageLocation(InternalFeatureOnOffOptions.LocalRegistryPath + nameof(OOP64Bit))); + public static readonly Option RemoteHostTest = new Option(nameof(InternalFeatureOnOffOptions), nameof(RemoteHostTest), defaultValue: false); } @@ -55,6 +60,7 @@ internal class RemoteHostOptionsProvider : IOptionProvider RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS, RemoteHostOptions.RequestServiceTimeoutInMS, RemoteHostOptions.RestartRemoteHostAllowed, + RemoteHostOptions.OOP64Bit, RemoteHostOptions.RemoteHostTest); } } diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs index 025062519d78b..b8029dd243b0c 100644 --- a/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs @@ -40,7 +40,7 @@ public void TestGetAssets() [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestCleanup() { - var storage = new AssetStorage(cleanupInterval: TimeSpan.FromMilliseconds(1), purgeAfter: TimeSpan.FromMilliseconds(2)); + var storage = new AssetStorage(cleanupInterval: TimeSpan.FromMilliseconds(1), purgeAfter: TimeSpan.FromMilliseconds(2), gcAfter: TimeSpan.FromMilliseconds(5)); var checksum = Checksum.Create(WellKnownSynchronizationKind.Null, ImmutableArray.CreateRange(Guid.NewGuid().ToByteArray())); var data = new object(); diff --git a/src/VisualStudio/Setup/VisualStudioSetup.csproj b/src/VisualStudio/Setup/VisualStudioSetup.csproj index 685358361fecc..86dd3242f7b58 100644 --- a/src/VisualStudio/Setup/VisualStudioSetup.csproj +++ b/src/VisualStudio/Setup/VisualStudioSetup.csproj @@ -252,6 +252,22 @@ PreserveNewest true + + PreserveNewest + true + + + PreserveNewest + true + + + PreserveNewest + true + + + PreserveNewest + true + diff --git a/src/VisualStudio/Setup/codeAnalysisService64.servicehub.service.json b/src/VisualStudio/Setup/codeAnalysisService64.servicehub.service.json new file mode 100644 index 0000000000000..5901a5bf5e8aa --- /dev/null +++ b/src/VisualStudio/Setup/codeAnalysisService64.servicehub.service.json @@ -0,0 +1,11 @@ +{ + "host": "desktopClr", + "hostId": "RoslynCodeAnalysisService", + "hostGroupAllowed": true, + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.CodeAnalysisService", + "appBasePath": "%VSAPPIDDIR%", + "configPath": "%PkgDefApplicationConfigFile%" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup/remoteHostService64.servicehub.service.json b/src/VisualStudio/Setup/remoteHostService64.servicehub.service.json new file mode 100644 index 0000000000000..0b436bfef5071 --- /dev/null +++ b/src/VisualStudio/Setup/remoteHostService64.servicehub.service.json @@ -0,0 +1,11 @@ +{ + "host": "desktopClr", + "hostId": "RoslynCodeAnalysisService", + "hostGroupAllowed": true, + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.RemoteHostService", + "appBasePath": "%VSAPPIDDIR%", + "configPath": "%PkgDefApplicationConfigFile%" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup/remoteSymbolSearchUpdateEngine64.servicehub.service.json b/src/VisualStudio/Setup/remoteSymbolSearchUpdateEngine64.servicehub.service.json new file mode 100644 index 0000000000000..99b3dca9b43a3 --- /dev/null +++ b/src/VisualStudio/Setup/remoteSymbolSearchUpdateEngine64.servicehub.service.json @@ -0,0 +1,11 @@ +{ + "host": "desktopClr", + "hostId": "RoslynCodeAnalysisService", + "hostGroupAllowed": true, + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.RemoteSymbolSearchUpdateEngine", + "appBasePath": "%VSAPPIDDIR%", + "configPath": "%PkgDefApplicationConfigFile%" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup/snapshotService64.servicehub.service.json b/src/VisualStudio/Setup/snapshotService64.servicehub.service.json new file mode 100644 index 0000000000000..8975e0b2cf6d1 --- /dev/null +++ b/src/VisualStudio/Setup/snapshotService64.servicehub.service.json @@ -0,0 +1,11 @@ +{ + "host": "desktopClr", + "hostId": "RoslynCodeAnalysisService", + "hostGroupAllowed": true, + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.SnapshotService", + "appBasePath": "%VSAPPIDDIR%", + "configPath": "%PkgDefApplicationConfigFile%" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup/source.extension.vsixmanifest b/src/VisualStudio/Setup/source.extension.vsixmanifest index b6cd7ddef7105..b06b60d9d93e3 100644 --- a/src/VisualStudio/Setup/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup/source.extension.vsixmanifest @@ -33,6 +33,10 @@ + + + + diff --git a/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs b/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs index 01afb176aebb2..2a005da41b04a 100644 --- a/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs +++ b/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs @@ -20,5 +20,6 @@ internal class DefaultExperimentationService : IExperimentationService internal static class WellKnownExperimentNames { public const string RoslynFeatureOOP = nameof(RoslynFeatureOOP); + public const string RoslynOOP64bit = nameof(RoslynOOP64bit); } } diff --git a/src/Workspaces/Core/Portable/Log/FunctionId.cs b/src/Workspaces/Core/Portable/Log/FunctionId.cs index a7781252a14c6..3e13cdff1df95 100644 --- a/src/Workspaces/Core/Portable/Log/FunctionId.cs +++ b/src/Workspaces/Core/Portable/Log/FunctionId.cs @@ -393,5 +393,7 @@ internal enum FunctionId SolutionCreator_AssetDifferences, Extension_InfoBar, Experiment_ABTesting, + AssetStorage_ForceGC, + RemoteHost_Bitness, } } diff --git a/src/Workspaces/Core/Portable/Remote/WellKnownRemoteHostServices.cs b/src/Workspaces/Core/Portable/Remote/WellKnownRemoteHostServices.cs index 353f1949441b1..fabdef5a59565 100644 --- a/src/Workspaces/Core/Portable/Remote/WellKnownRemoteHostServices.cs +++ b/src/Workspaces/Core/Portable/Remote/WellKnownRemoteHostServices.cs @@ -2,8 +2,13 @@ namespace Microsoft.CodeAnalysis.Remote { - internal class WellKnownRemoteHostServices + internal static class WellKnownRemoteHostServices { - public const string RemoteHostService = "remoteHostService"; + public static void Set64bit(bool x64) + { + RemoteHostService = "remoteHostService" + (x64 ? "64" : ""); + } + + public static string RemoteHostService { get; private set; } = "remoteHostService"; } } diff --git a/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubServices.cs b/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubServices.cs index 952c4ce75ae91..028b367f80821 100644 --- a/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubServices.cs +++ b/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubServices.cs @@ -4,11 +4,18 @@ namespace Microsoft.CodeAnalysis.Remote { internal static class WellKnownServiceHubServices { - public const string ServiceHubServiceBase_Initialize = "Initialize"; + public static void Set64bit(bool x64) + { + var bit = x64 ? "64" : ""; + + SnapshotService = "snapshotService" + bit; + CodeAnalysisService = "codeAnalysisService" + bit; + RemoteSymbolSearchUpdateEngine = "remoteSymbolSearchUpdateEngine" + bit; + } - public const string SnapshotService = "snapshotService"; - public const string CodeAnalysisService = "codeAnalysisService"; - public const string RemoteSymbolSearchUpdateEngine = "remoteSymbolSearchUpdateEngine"; + public static string SnapshotService { get; private set; } = "snapshotService"; + public static string CodeAnalysisService { get; private set; } = "codeAnalysisService"; + public static string RemoteSymbolSearchUpdateEngine { get; private set; } = "remoteSymbolSearchUpdateEngine"; // CodeLens methods. public const string CodeAnalysisService_GetReferenceCountAsync = "GetReferenceCountAsync"; @@ -16,8 +23,9 @@ internal static class WellKnownServiceHubServices public const string CodeAnalysisService_FindReferenceMethodsAsync = "FindReferenceMethodsAsync"; public const string CodeAnalysisService_GetFullyQualifiedName = "GetFullyQualifiedName"; - public const string CodeAnalysisService_CalculateDiagnosticsAsync = "CalculateDiagnosticsAsync"; - + public const string ServiceHubServiceBase_Initialize = "Initialize"; public const string AssetService_RequestAssetAsync = "RequestAssetAsync"; + + public const string CodeAnalysisService_CalculateDiagnosticsAsync = "CalculateDiagnosticsAsync"; } } diff --git a/src/Workspaces/Remote/Core/Services/AssetStorage.cs b/src/Workspaces/Remote/Core/Services/AssetStorage.cs index 3d690be1f8f88..a830b36659f9d 100644 --- a/src/Workspaces/Remote/Core/Services/AssetStorage.cs +++ b/src/Workspaces/Remote/Core/Services/AssetStorage.cs @@ -18,28 +18,59 @@ namespace Microsoft.CodeAnalysis.Remote internal class AssetStorage { // TODO: think of a way to use roslyn option service in OOP - public static readonly AssetStorage Default = new AssetStorage(cleanupInterval: TimeSpan.FromMinutes(1), purgeAfter: TimeSpan.FromMinutes(3)); + public static readonly AssetStorage Default = + new AssetStorage(cleanupInterval: TimeSpan.FromMinutes(1), purgeAfter: TimeSpan.FromMinutes(3), gcAfter: TimeSpan.FromMinutes(5)); + /// + /// Time interval we check storage for cleanup + /// private readonly TimeSpan _cleanupIntervalTimeSpan; + + /// + /// Time span data can sit inside of cache () without being used. + /// after that, it will be removed from the cache. + /// private readonly TimeSpan _purgeAfterTimeSpan; + /// + /// Time we will wait after the last activity before doing explicit GC cleanup. + /// We monitor all resource access and service call to track last activity time. + /// + /// We do this since 64bit process can hold onto quite big unused memory when + /// OOP is running as AnyCpu + /// + private readonly TimeSpan _gcAfterTimeSpan; + private readonly ConcurrentDictionary _globalAssets = new ConcurrentDictionary(concurrencyLevel: 4, capacity: 10); private readonly ConcurrentDictionary _assets = new ConcurrentDictionary(concurrencyLevel: 4, capacity: 10); + private DateTime _lastGCRun; + private DateTime _lastActivityTime; + private volatile AssetSource _assetSource; + // constructor for testing public AssetStorage() { - // constructor for testing } - public AssetStorage(TimeSpan cleanupInterval, TimeSpan purgeAfter) + /// + /// Create central data cache + /// + /// time interval to clean up + /// time unused data can sit in the cache + /// time we wait before it call GC since last activity + public AssetStorage(TimeSpan cleanupInterval, TimeSpan purgeAfter, TimeSpan gcAfter) { _cleanupIntervalTimeSpan = cleanupInterval; _purgeAfterTimeSpan = purgeAfter; + _gcAfterTimeSpan = gcAfter; + + _lastActivityTime = DateTime.UtcNow; + _lastGCRun = DateTime.UtcNow; Task.Run(CleanAssetsAsync, CancellationToken.None); } @@ -56,16 +87,22 @@ public void SetAssetSource(AssetSource assetSource) public bool TryAddGlobalAsset(Checksum checksum, object value) { + UpdateLastActivityTime(); + return _globalAssets.TryAdd(checksum, new Entry(value)); } public bool TryAddAsset(Checksum checksum, object value) { + UpdateLastActivityTime(); + return _assets.TryAdd(checksum, new Entry(value)); } public IEnumerable GetGlobalAssetsOfType(CancellationToken cancellationToken) { + UpdateLastActivityTime(); + foreach (var asset in _globalAssets) { cancellationToken.ThrowIfCancellationRequested(); @@ -80,6 +117,8 @@ public IEnumerable GetGlobalAssetsOfType(CancellationToken cancellationTok public bool TryGetAsset(Checksum checksum, out T value) { + UpdateLastActivityTime(); + value = default(T); using (Logger.LogBlock(FunctionId.AssetStorage_TryGetAsset, Checksum.GetChecksumLogInfo, checksum, CancellationToken.None)) { @@ -97,6 +136,11 @@ public bool TryGetAsset(Checksum checksum, out T value) } } + public void UpdateLastActivityTime() + { + _lastActivityTime = DateTime.UtcNow; + } + private void Update(Checksum checksum, Entry entry) { // entry is reference type. we update it directly. @@ -110,14 +154,50 @@ private async Task CleanAssetsAsync() { CleanAssets(); + ForceGC(); + await Task.Delay(_cleanupIntervalTimeSpan).ConfigureAwait(false); } } - private void CleanAssets() + private void ForceGC() { + // if there was no activity since last GC run. we don't have anything to do + if (_lastGCRun >= _lastActivityTime) + { + return; + } + var current = DateTime.UtcNow; + if (current - _lastActivityTime < _gcAfterTimeSpan) + { + // we are having activities. + return; + } + + using (Logger.LogBlock(FunctionId.AssetStorage_ForceGC, CancellationToken.None)) + { + // we didn't have activity for 5 min. spend some time to drop + // unused memory + for (var i = 0; i < 3; i++) + { + GC.Collect(); + } + } + // update gc run time + _lastGCRun = current; + } + + private void CleanAssets() + { + if (_assets.Count == 0) + { + // no asset, nothing to do. + return; + } + + var current = DateTime.UtcNow; using (Logger.LogBlock(FunctionId.AssetStorage_CleanAssets, CancellationToken.None)) { foreach (var kvp in _assets.ToArray()) diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs index 2ce1c221c7f74..b869caa5a82ef 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.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.Concurrent; +using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -60,7 +61,11 @@ private async Task SerializeDiagnosticResultAsync(string streamName, DiagnosticA { using (var writer = new ObjectWriter(stream)) { - DiagnosticResultSerializer.Serialize(writer, result, cancellationToken); + var info = DiagnosticResultSerializer.Serialize(writer, result, cancellationToken); + + // save log for debugging + Log(TraceEventType.Information, $"diagnostics: {info.diagnostics}, telemetry: {info.telemetry}, exceptions: {info.exceptions}"); + } await stream.FlushAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs index 0023436652cee..975d3ea27cc02 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/ServiceHubServiceBase.cs @@ -172,6 +172,8 @@ private static Task GetSolutionAsync(RoslynServices roslynService, Pin protected async Task RunServiceAsync(Func> callAsync, CancellationToken cancellationToken) { + AssetStorage.UpdateLastActivityTime(); + try { return await callAsync().ConfigureAwait(false); @@ -185,6 +187,8 @@ protected async Task RunServiceAsync(Func> callAsync, Cancellation protected async Task RunServiceAsync(Func callAsync, CancellationToken cancellationToken) { + AssetStorage.UpdateLastActivityTime(); + try { await callAsync().ConfigureAwait(false); @@ -198,6 +202,8 @@ protected async Task RunServiceAsync(Func callAsync, CancellationToken can protected T RunService(Func call, CancellationToken cancellationToken) { + AssetStorage.UpdateLastActivityTime(); + try { return call(); @@ -211,6 +217,8 @@ protected T RunService(Func call, CancellationToken cancellationToken) protected void RunService(Action call, CancellationToken cancellationToken) { + AssetStorage.UpdateLastActivityTime(); + try { call();