Merge branch 'netcore/dev' into netcore/feature/AB3677-remove-current-from-core
# Conflicts: # src/Umbraco.Abstractions/UriExtensions.cs # src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs # src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs # src/Umbraco.Web/Editors/MediaController.cs # src/Umbraco.Web/Editors/TemplateController.cs # src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs # src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs # src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
This commit is contained in:
@@ -199,7 +199,7 @@ namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
var task = _runningTask ?? Task.FromResult(0);
|
||||
var task = _runningTask ?? Task.CompletedTask;
|
||||
return new ThreadingTaskImmutable(task);
|
||||
}
|
||||
}
|
||||
@@ -211,8 +211,9 @@ namespace Umbraco.Web.Scheduling
|
||||
/// <returns>An awaitable object.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Used to wait until the runner has terminated.</para>
|
||||
/// <para>This is for unit tests and should not be used otherwise. In most cases when the runner
|
||||
/// has terminated, the application domain is going down and it is not the right time to do things.</para>
|
||||
/// <para>
|
||||
/// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal ThreadingTaskImmutable TerminatedAwaitable
|
||||
{
|
||||
@@ -338,29 +339,37 @@ namespace Umbraco.Web.Scheduling
|
||||
if (_isRunning == false) return; // done already
|
||||
}
|
||||
|
||||
var hasTasks = TaskCount > 0;
|
||||
|
||||
if (!force && hasTasks)
|
||||
_logger.Info<BackgroundTaskRunner>("{LogPrefix} Waiting for tasks to complete", _logPrefix);
|
||||
|
||||
// complete the queue
|
||||
// will stop waiting on the queue or on a latch
|
||||
_tasks.Complete();
|
||||
|
||||
if (force)
|
||||
{
|
||||
// we must bring everything down, now
|
||||
Thread.Sleep(100); // give time to Complete()
|
||||
// we must bring everything down, now
|
||||
lock (_locker)
|
||||
{
|
||||
// was Complete() enough?
|
||||
if (_isRunning == false) return;
|
||||
// if _tasks.Complete() ended up triggering code to stop the runner and reset
|
||||
// the _isRunning flag, then there's no need to initiate a cancel on the cancelation token.
|
||||
if (_isRunning == false)
|
||||
return;
|
||||
}
|
||||
|
||||
// try to cancel running async tasks (cannot do much about sync tasks)
|
||||
// break latched tasks
|
||||
// stop processing the queue
|
||||
_shutdownTokenSource.Cancel(false); // false is the default
|
||||
_shutdownTokenSource.Dispose();
|
||||
_shutdownTokenSource?.Cancel(false); // false is the default
|
||||
_shutdownTokenSource?.Dispose();
|
||||
_shutdownTokenSource = null;
|
||||
}
|
||||
|
||||
// tasks in the queue will be executed...
|
||||
if (wait == false) return;
|
||||
if (!wait) return;
|
||||
|
||||
_runningTask?.Wait(CancellationToken.None); // wait for whatever is running to end...
|
||||
}
|
||||
@@ -428,7 +437,7 @@ namespace Umbraco.Web.Scheduling
|
||||
lock (_locker)
|
||||
{
|
||||
// deal with race condition
|
||||
if (_shutdownToken.IsCancellationRequested == false && _tasks.Count > 0) continue;
|
||||
if (_shutdownToken.IsCancellationRequested == false && TaskCount > 0) continue;
|
||||
|
||||
// if we really have nothing to do, stop
|
||||
_logger.Debug<BackgroundTaskRunner>("{LogPrefix} Stopping", _logPrefix);
|
||||
@@ -453,7 +462,7 @@ namespace Umbraco.Web.Scheduling
|
||||
// if KeepAlive is false then don't block, exit if there is
|
||||
// no task in the buffer - yes, there is a race condition, which
|
||||
// we'll take care of
|
||||
if (_options.KeepAlive == false && _tasks.Count == 0)
|
||||
if (_options.KeepAlive == false && TaskCount == 0)
|
||||
return null;
|
||||
|
||||
try
|
||||
@@ -503,15 +512,19 @@ namespace Umbraco.Web.Scheduling
|
||||
// returns the task that completed
|
||||
// - latched.Latch completes when the latch releases
|
||||
// - _tasks.Completion completes when the runner completes
|
||||
// - tokenTaskSource.Task completes when this task, or the whole runner, is cancelled
|
||||
// - tokenTaskSource.Task completes when this task, or the whole runner is cancelled
|
||||
var task = await Task.WhenAny(latched.Latch, _tasks.Completion, tokenTaskSource.Task);
|
||||
|
||||
// ok to run now
|
||||
if (task == latched.Latch)
|
||||
return bgTask;
|
||||
|
||||
// we are shutting down if the _tasks.Complete(); was called or the shutdown token was cancelled
|
||||
var isShuttingDown = _shutdownToken.IsCancellationRequested || task == _tasks.Completion;
|
||||
|
||||
// if shutting down, return the task only if it runs on shutdown
|
||||
if (_shutdownToken.IsCancellationRequested == false && latched.RunsOnShutdown) return bgTask;
|
||||
if (isShuttingDown && latched.RunsOnShutdown)
|
||||
return bgTask;
|
||||
|
||||
// else, either it does not run on shutdown or it's been cancelled, dispose
|
||||
latched.Dispose();
|
||||
@@ -578,17 +591,18 @@ namespace Umbraco.Web.Scheduling
|
||||
// triggers when the hosting environment requests that the runner terminates
|
||||
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Terminating;
|
||||
|
||||
// triggers when the runner has terminated (no task can be added, no task is running)
|
||||
// triggers when the hosting environment has terminated (no task can be added, no task is running)
|
||||
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Terminated;
|
||||
|
||||
private void OnEvent(TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> handler, string name)
|
||||
{
|
||||
if (handler == null) return;
|
||||
OnEvent(handler, name, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnEvent<TArgs>(TypedEventHandler<BackgroundTaskRunner<T>, TArgs> handler, string name, TArgs e)
|
||||
{
|
||||
_logger.Debug<BackgroundTaskRunner>("{LogPrefix} OnEvent {EventName}", _logPrefix, name);
|
||||
|
||||
if (handler == null) return;
|
||||
|
||||
try
|
||||
@@ -664,17 +678,16 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegisteredObject.Stop
|
||||
|
||||
/// <summary>
|
||||
/// Requests a registered object to un-register.
|
||||
/// Used by IRegisteredObject.Stop and shutdown on threadpool threads to not block shutdown times.
|
||||
/// </summary>
|
||||
/// <param name="immediate">true to indicate the registered object should un-register from the hosting
|
||||
/// environment before returning; otherwise, false.</param>
|
||||
/// <remarks>
|
||||
/// <para>"When the application manager needs to stop a registered object, it will call the Stop method."</para>
|
||||
/// <para>The application manager will call the Stop method to ask a registered object to un-register. During
|
||||
/// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method.</para>
|
||||
/// </remarks>
|
||||
public void Stop(bool immediate)
|
||||
/// <param name="immediate"></param>
|
||||
/// <returns>
|
||||
/// An awaitable Task that is used to handle the shutdown.
|
||||
/// </returns>
|
||||
internal Task StopInternal(bool immediate)
|
||||
{
|
||||
// the first time the hosting environment requests that the runner terminates,
|
||||
// raise the Terminating event - that could be used to prevent any process that
|
||||
@@ -693,33 +706,90 @@ namespace Umbraco.Web.Scheduling
|
||||
if (onTerminating)
|
||||
OnEvent(Terminating, "Terminating");
|
||||
|
||||
if (immediate == false)
|
||||
// Run the Stop commands on another thread since IRegisteredObject.Stop calls are called sequentially
|
||||
// with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop.
|
||||
if (!immediate)
|
||||
{
|
||||
// The Stop method is first called with the immediate parameter set to false. The object can either complete
|
||||
// processing, call the UnregisterObject method, and then return or it can return immediately and complete
|
||||
// processing asynchronously before calling the UnregisterObject method.
|
||||
return Task.Run(StopInitial, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_terminated) return Task.CompletedTask;
|
||||
return Task.Run(StopImmediate, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Info<BackgroundTaskRunner>("{LogPrefix} Waiting for tasks to complete", _logPrefix);
|
||||
/// <summary>
|
||||
/// Requests a registered object to un-register.
|
||||
/// </summary>
|
||||
/// <param name="immediate">true to indicate the registered object should un-register from the hosting
|
||||
/// environment before returning; otherwise, false.</param>
|
||||
/// <remarks>
|
||||
/// <para>"When the application manager needs to stop a registered object, it will call the Stop method."</para>
|
||||
/// <para>The application manager will call the Stop method to ask a registered object to un-register. During
|
||||
/// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method.</para>
|
||||
/// </remarks>
|
||||
public void Stop(bool immediate) => StopInternal(immediate);
|
||||
|
||||
/// <summary>
|
||||
/// Called when immediate == false for IRegisteredObject.Stop(bool immediate)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Called on a threadpool thread
|
||||
/// </remarks>
|
||||
private void StopInitial()
|
||||
{
|
||||
// immediate == false when the app is trying to wind down, immediate == true will be called either:
|
||||
// after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop.
|
||||
// So Stop may be called twice or sometimes only once.
|
||||
|
||||
try
|
||||
{
|
||||
Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// raise the completed event only after the running threading task has completed
|
||||
lock (_locker)
|
||||
{
|
||||
if (_runningTask != null)
|
||||
_runningTask.ContinueWith(_ => Terminate(false));
|
||||
_runningTask.ContinueWith(_ => StopImmediate());
|
||||
else
|
||||
Terminate(false);
|
||||
StopImmediate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the registered object does not complete processing before the application manager's time-out
|
||||
// period expires, the Stop method is called again with the immediate parameter set to true. When the
|
||||
// immediate parameter is true, the registered object must call the UnregisterObject method before returning;
|
||||
// otherwise, its registration will be removed by the application manager.
|
||||
|
||||
_logger.Info<BackgroundTaskRunner>("{LogPrefix} Canceling tasks", _logPrefix);
|
||||
// If the shutdown token was not canceled in the Shutdown call above, it means there was still tasks
|
||||
// being processed, in which case we'll give it a couple seconds
|
||||
if (!_shutdownToken.IsCancellationRequested)
|
||||
{
|
||||
// If we are called with immediate == false, wind down above and then shutdown within 2 seconds,
|
||||
// we want to shut down the app as quick as possible, if we wait until immediate == true, this can
|
||||
// take a very long time since immediate will only be true when a new request is received on the new
|
||||
// appdomain (or another iis timeout occurs ... which can take some time).
|
||||
Thread.Sleep(2000); //we are already on a threadpool thread
|
||||
StopImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when immediate == true for IRegisteredObject.Stop(bool immediate)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Called on a threadpool thread
|
||||
/// </remarks>
|
||||
private void StopImmediate()
|
||||
{
|
||||
_logger.Info<BackgroundTaskRunner>("{LogPrefix} Canceling tasks", _logPrefix);
|
||||
try
|
||||
{
|
||||
Shutdown(true, true); // cancel all tasks, wait for the current one to end
|
||||
}
|
||||
finally
|
||||
{
|
||||
Terminate(true);
|
||||
}
|
||||
}
|
||||
@@ -732,7 +802,13 @@ namespace Umbraco.Web.Scheduling
|
||||
// raise the Terminated event
|
||||
// complete the awaitable completion source, if any
|
||||
|
||||
HostingEnvironment.UnregisterObject(this);
|
||||
if (immediate)
|
||||
{
|
||||
//only unregister when it's the final call, else we won't be notified of the final call
|
||||
HostingEnvironment.UnregisterObject(this);
|
||||
}
|
||||
|
||||
if (_terminated) return; // already taken care of
|
||||
|
||||
TaskCompletionSource<int> terminatedSource;
|
||||
lock (_locker)
|
||||
@@ -747,7 +823,9 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
OnEvent(Terminated, "Terminated");
|
||||
|
||||
terminatedSource.SetResult(0);
|
||||
terminatedSource.TrySetResult(0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Sync;
|
||||
|
||||
@@ -11,14 +13,16 @@ namespace Umbraco.Web.Scheduling
|
||||
internal class KeepAlive : RecurringTaskBase
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IKeepAliveSection _keepAliveSection;
|
||||
private readonly IProfilingLogger _logger;
|
||||
private static HttpClient _httpClient;
|
||||
|
||||
public KeepAlive(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
IRuntimeState runtime, IProfilingLogger logger)
|
||||
IRuntimeState runtime, IKeepAliveSection keepAliveSection, IProfilingLogger logger)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_keepAliveSection = keepAliveSection;
|
||||
_logger = logger;
|
||||
if (_httpClient == null)
|
||||
_httpClient = new HttpClient();
|
||||
@@ -46,25 +50,27 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
using (_logger.DebugDuration<KeepAlive>("Keep alive executing", "Keep alive complete"))
|
||||
{
|
||||
string umbracoAppUrl = null;
|
||||
|
||||
var keepAlivePingUrl = _keepAliveSection.KeepAlivePingUrl;
|
||||
try
|
||||
{
|
||||
umbracoAppUrl = _runtime.ApplicationUrl.ToString();
|
||||
if (umbracoAppUrl.IsNullOrWhiteSpace())
|
||||
if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}"))
|
||||
{
|
||||
_logger.Warn<KeepAlive>("No url for service (yet), skip.");
|
||||
return true; // repeat
|
||||
var umbracoAppUrl = _runtime.ApplicationUrl.ToString();
|
||||
if (umbracoAppUrl.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Warn<KeepAlive>("No umbracoApplicationUrl for service (yet), skip.");
|
||||
return true; // repeat
|
||||
}
|
||||
|
||||
keepAlivePingUrl = keepAlivePingUrl.Replace("{umbracoApplicationUrl}", umbracoAppUrl.TrimEnd('/'));
|
||||
}
|
||||
|
||||
var url = umbracoAppUrl.TrimEnd('/') + "/api/keepalive/ping";
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, keepAlivePingUrl);
|
||||
var result = await _httpClient.SendAsync(request, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<KeepAlive>(ex, "Keep alive failed (at '{UmbracoAppUrl}').", umbracoAppUrl);
|
||||
_logger.Error<KeepAlive>(ex, "Keep alive failed (at '{keepAlivePingUrl}').", keepAlivePingUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,11 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
var tasks = new List<IBackgroundTask>();
|
||||
|
||||
tasks.Add(RegisterKeepAlive());
|
||||
if (settings.KeepAlive.DisableKeepAliveTask == false)
|
||||
{
|
||||
tasks.Add(RegisterKeepAlive(settings.KeepAlive));
|
||||
}
|
||||
|
||||
tasks.Add(RegisterScheduledPublishing());
|
||||
tasks.Add(RegisterLogScrubber(settings));
|
||||
tasks.Add(RegisterTempFileCleanup());
|
||||
@@ -111,11 +115,11 @@ namespace Umbraco.Web.Scheduling
|
||||
});
|
||||
}
|
||||
|
||||
private IBackgroundTask RegisterKeepAlive()
|
||||
private IBackgroundTask RegisterKeepAlive(IKeepAliveSection keepAliveSection)
|
||||
{
|
||||
// ping/keepalive
|
||||
// on all servers
|
||||
var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, _logger);
|
||||
var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSection, _logger);
|
||||
_keepAliveRunner.TryAdd(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user