/// <summary> /// It's recommended to use this method (or another TryAwaitOrTimeout or AwaitOrTimeout method) /// instead of just Task.WhenAny(taskToAwait, Task.Delay(timeout)) /// because this method cancels the timer for timeout while <c>Task.Delay(timeout)</c>. /// If the number of “zombie” timer jobs starts becoming significant, performance could suffer. /// For more detailed explanation see https://devblogs.microsoft.com/pfxteam/crafting-a-task-timeoutafter-method/ /// </summary> /// <returns><c>true</c> if <c>taskToAwait</c> completed before the timeout, <c>false</c> otherwise</returns> internal static async Task <bool> TryAwaitOrTimeout(this IAgentTimer agentTimer, Task taskToAwait , AgentTimeInstant until, CancellationToken cancellationToken = default ) { var timeoutDelayCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var timeoutDelayTask = agentTimer.Delay(until, timeoutDelayCts.Token); try { var completedTask = await Task.WhenAny(taskToAwait, timeoutDelayTask); if (completedTask == taskToAwait) { // await taskToAwait to make it throw if taskToAwait is faulted or cancelled so it will throw as it should await taskToAwait; return(true); } Assertion.IfEnabled?.That(completedTask == timeoutDelayTask , $"{nameof(completedTask)}: {completedTask}, {nameof(timeoutDelayTask)}: timeOutTask, {nameof(taskToAwait)}: taskToAwait"); // await timeout task in case it is cancelled and did not timed out so it will throw as it should await timeoutDelayTask; return(false); } finally { if (!timeoutDelayTask.IsCompleted) { timeoutDelayCts.Cancel(); } timeoutDelayCts.Dispose(); } }
internal CentralConfigFetcher(IApmLogger logger, IConfigStore configStore, ICentralConfigResponseParser centralConfigResponseParser, Service service, HttpMessageHandler httpMessageHandler = null, IAgentTimer agentTimer = null, string dbgName = null ) : this(logger, configStore, configStore.CurrentSnapshot, service, httpMessageHandler, agentTimer, dbgName) { _centralConfigResponseParser = centralConfigResponseParser; }
/// <summary> /// We need this private ctor to avoid calling configStore.CurrentSnapshot twice (and thus possibly using different /// snapshots) /// when passing isEnabled: initialConfigSnapshot.CentralConfig and config: initialConfigSnapshot to base /// </summary> private CentralConfigFetcher(IApmLogger logger, IConfigStore configStore, IConfigSnapshot initialConfigSnapshot, Service service , HttpMessageHandler httpMessageHandler, IAgentTimer agentTimer, string dbgName ) : base(/* isEnabled: */ initialConfigSnapshot.CentralConfig, logger, ThisClassName, service, initialConfigSnapshot, httpMessageHandler) { _logger = logger?.Scoped(ThisClassName + (dbgName == null ? "" : $" (dbgName: `{dbgName}')")); _initialSnapshot = initialConfigSnapshot; var isCentralConfigOptEqDefault = _initialSnapshot.CentralConfig == ConfigConsts.DefaultValues.CentralConfig; var centralConfigStatus = _initialSnapshot.CentralConfig ? "enabled" : "disabled"; if (!isCentralConfigOptEqDefault) { centralConfigStatus = centralConfigStatus.ToUpper(); } _logger.IfLevel(isCentralConfigOptEqDefault ? LogLevel.Debug : LogLevel.Information) ?.Log("Central configuration feature is {CentralConfigStatus} because CentralConfig option's value is {CentralConfigOptionValue}" + " (default value is {CentralConfigOptionDefaultValue})" , centralConfigStatus, _initialSnapshot.CentralConfig, ConfigConsts.DefaultValues.CentralConfig); if (!_initialSnapshot.CentralConfig) { return; } _configStore = configStore; _agentTimer = agentTimer ?? new AgentTimer(); _getConfigAbsoluteUrl = BackendCommUtils.ApmServerEndpoints.BuildGetConfigAbsoluteUrl(initialConfigSnapshot.ServerUrls.First(), service); _logger.Debug() ?.Log("Combined absolute URL for APM Server get central configuration endpoint: `{Url}'. Service: {Service}." , _getConfigAbsoluteUrl, service); StartWorkLoop(); }
internal bool IsCompatibleWith(IAgentTimer otherAgentTimer) => _sourceAgentTimer == otherAgentTimer;
internal AgentTimeInstant(IAgentTimer sourceAgentTimer, TimeSpan elapsedSinceTimerStarted) { _sourceAgentTimer = sourceAgentTimer; _elapsedSinceTimerStarted = elapsedSinceTimerStarted; }
/// <summary> /// It's recommended to use this method (or another TryAwaitOrTimeout or AwaitOrTimeout method) /// instead of just Task.WhenAny(taskToAwait, Task.Delay(timeout)) /// because this method cancels the timer for timeout while <c>Task.Delay(timeout)</c>. /// If the number of “zombie” timer jobs starts becoming significant, performance could suffer. /// For more detailed explanation see https://devblogs.microsoft.com/pfxteam/crafting-a-task-timeoutafter-method/ /// </summary> /// <returns> /// (<c>true</c>, result of <c>taskToAwait</c>) if <c>taskToAwait</c> completed before the timeout, <c>false</c> /// otherwise /// </returns> internal static async Task <ValueTuple <bool, TResult> > TryAwaitOrTimeout <TResult>(this IAgentTimer agentTimer, Task <TResult> taskToAwait , AgentTimeInstant until, CancellationToken cancellationToken = default ) { var hasTaskToAwaitCompletedBeforeTimeout = await TryAwaitOrTimeout(agentTimer, (Task)taskToAwait, until, cancellationToken); return(hasTaskToAwaitCompletedBeforeTimeout, hasTaskToAwaitCompletedBeforeTimeout ? await taskToAwait : default);
internal CentralConfigFetcher(IApmLogger logger, IConfigStore configStore, Service service , HttpMessageHandler httpMessageHandler = null, IAgentTimer agentTimer = null, string dbgName = null ) : this(logger, configStore, new CentralConfigResponseParser(logger), service, httpMessageHandler, agentTimer, dbgName) { }