From 5021428119b10fef35f196164534063a4930f5f9 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Mon, 23 Jul 2018 14:19:58 +0300 Subject: [PATCH] Added BackgroundWorkers project. --- framework/Volo.Abp.sln | 7 + .../Volo.Abp.BackgroundWorkers.csproj | 20 +++ .../AbpBackgroundWorkersModule.cs | 17 +++ .../BackgroundWorkers/BackgroundWorkerBase.cs | 47 ++++++ .../BackgroundWorkerManager.cs | 68 +++++++++ .../BackgroundWorkers/IBackgroundWorker.cs | 12 ++ .../IBackgroundWorkerManager.cs | 18 +++ .../PeriodicBackgroundWorkerBase.cs | 62 ++++++++ .../Volo/Abp/Threading/AbpTimer.cs | 141 ++++++++++++++++++ .../Volo/Abp/Threading/IRunnable.cs | 25 ++++ .../Volo/Abp/Threading/RunnableBase.cs | 29 ++++ .../Volo/Abp/Threading/RunnableExtensions.cs | 17 +++ 12 files changed, 463 insertions(+) create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs create mode 100644 framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index f346cec27c..bc7a1bf35e 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -196,6 +196,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundJobs", "src\Volo.Abp.BackgroundJobs\Volo.Abp.BackgroundJobs.csproj", "{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundWorkers", "src\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj", "{6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -550,6 +552,10 @@ Global {E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Release|Any CPU.Build.0 = Release|Any CPU + {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -644,6 +650,7 @@ Global {CAE68246-70A8-4E87-9B83-A9F7DA343E5E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {27C120C9-F618-4C1D-B959-8D0B048D0835} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj b/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj new file mode 100644 index 0000000000..61bc5af316 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.BackgroundWorkers + Volo.Abp.BackgroundWorkers + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs new file mode 100644 index 0000000000..0d2cb421ff --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + [DependsOn( + typeof(AbpThreadingModule) + )] + public class AbpBackgroundWorkersModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAssemblyOf(); + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs new file mode 100644 index 0000000000..b2ae62fc6e --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + /// + /// Base class that can be used to implement . + /// + public abstract class BackgroundWorkerBase : RunnableBase, IBackgroundWorker + { + //TODO: Add UOW, Localization and other useful properties..? + + public ILogger Logger { protected get; set; } + + protected BackgroundWorkerBase() + { + Logger = NullLogger.Instance; + } + + public override void Start() + { + Logger.LogDebug("Starting background worker: " + ToString()); + base.Start(); + Logger.LogDebug("Started background worker: " + ToString()); + } + + public override void Stop() + { + Logger.LogDebug("Stopping background worker: " + ToString()); + base.Stop(); + Logger.LogDebug("Stopped background worker: " + ToString()); + } + + public override void WaitToStop() + { + Logger.LogDebug("Waiting background worker to completely stop: " + ToString()); + base.WaitToStop(); + Logger.LogDebug("Background worker is completely stopped: " + ToString()); + } + + public override string ToString() + { + return GetType().FullName; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs new file mode 100644 index 0000000000..6c5f890f12 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + /// + /// Implements . + /// + public class BackgroundWorkerManager : RunnableBase, IBackgroundWorkerManager, ISingletonDependency, IDisposable + { + private readonly List _backgroundJobs; + + /// + /// Initializes a new instance of the class. + /// + public BackgroundWorkerManager() + { + _backgroundJobs = new List(); + } + + public override void Start() + { + base.Start(); + + _backgroundJobs.ForEach(job => job.Start()); + } + + public override void Stop() + { + _backgroundJobs.ForEach(job => job.Stop()); + + base.Stop(); + } + + public override void WaitToStop() + { + _backgroundJobs.ForEach(job => job.WaitToStop()); + + base.WaitToStop(); + } + + public void Add(IBackgroundWorker worker) + { + _backgroundJobs.Add(worker); + + if (IsRunning) + { + worker.Start(); + } + } + + private bool _isDisposed; + + public void Dispose() + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + //TODO: ??? + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs new file mode 100644 index 0000000000..1aec158155 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + /// + /// Interface for a worker (thread) that runs on background to perform some tasks. + /// + public interface IBackgroundWorker : IRunnable + { + + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs new file mode 100644 index 0000000000..8465586cb3 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs @@ -0,0 +1,18 @@ +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + /// + /// Used to manage background workers. + /// + public interface IBackgroundWorkerManager : IRunnable + { + /// + /// Adds a new worker. Starts the worker immediately if has started. + /// + /// + /// The worker. It should be resolved from IOC. + /// + void Add(IBackgroundWorker worker); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs new file mode 100644 index 0000000000..38d8448c72 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.Extensions.Logging; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers +{ + /// + /// Extends to add a periodic running Timer. + /// + public abstract class PeriodicBackgroundWorkerBase : BackgroundWorkerBase + { + protected readonly AbpTimer Timer; + + /// + /// Initializes a new instance of the class. + /// + /// A timer. + protected PeriodicBackgroundWorkerBase(AbpTimer timer) + { + Timer = timer; + Timer.Elapsed += Timer_Elapsed; + } + + public override void Start() + { + base.Start(); + Timer.Start(); + } + + public override void Stop() + { + Timer.Stop(); + base.Stop(); + } + + public override void WaitToStop() + { + Timer.WaitToStop(); + base.WaitToStop(); + } + + /// + /// Handles the Elapsed event of the Timer. + /// + private void Timer_Elapsed(object sender, System.EventArgs e) + { + try + { + DoWork(); + } + catch (Exception ex) + { + Logger.LogException(ex); + } + } + + /// + /// Periodic works should be done by implementing this method. + /// + protected abstract void DoWork(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs new file mode 100644 index 0000000000..c61dd645f4 --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs @@ -0,0 +1,141 @@ +using System; +using System.Threading; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Threading +{ + /// + /// A roboust timer implementation that ensures no overlapping occurs. It waits exactly specified between ticks. + /// + public class AbpTimer : RunnableBase, ITransientDependency + { + /// + /// This event is raised periodically according to Period of Timer. + /// + public event EventHandler Elapsed; + + /// + /// Task period of timer (as milliseconds). + /// + public int Period { get; set; } + + /// + /// Indicates whether timer raises Elapsed event on Start method of Timer for once. + /// Default: False. + /// + public bool RunOnStart { get; set; } + + /// + /// This timer is used to perfom the task at spesified intervals. + /// + private readonly Timer _taskTimer; + + /// + /// Indicates that whether timer is running or stopped. + /// + private volatile bool _running; + + /// + /// Indicates that whether performing the task or _taskTimer is in sleep mode. + /// This field is used to wait executing tasks when stopping Timer. + /// + private volatile bool _performingTasks; + + /// + /// Creates a new Timer. + /// + public AbpTimer() + { + _taskTimer = new Timer(TimerCallBack, null, Timeout.Infinite, Timeout.Infinite); + } + + /// + /// Starts the timer. + /// + public override void Start() + { + if (Period <= 0) + { + throw new AbpException("Period should be set before starting the timer!"); + } + + base.Start(); + + _running = true; + _taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite); + } + + /// + /// Stops the timer. + /// + public override void Stop() + { + lock (_taskTimer) + { + _running = false; + _taskTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + base.Stop(); + } + + /// + /// Waits the service to stop. + /// + public override void WaitToStop() + { + lock (_taskTimer) + { + while (_performingTasks) + { + Monitor.Wait(_taskTimer); + } + } + + base.WaitToStop(); + } + + /// + /// This method is called by _taskTimer. + /// + /// Not used argument + private void TimerCallBack(object state) + { + lock (_taskTimer) + { + if (!_running || _performingTasks) + { + return; + } + + _taskTimer.Change(Timeout.Infinite, Timeout.Infinite); + _performingTasks = true; + } + + try + { + if (Elapsed != null) + { + Elapsed(this, new EventArgs()); + } + } + catch + { + + } + finally + { + lock (_taskTimer) + { + _performingTasks = false; + if (_running) + { + _taskTimer.Change(Period, Timeout.Infinite); + } + + Monitor.Pulse(_taskTimer); + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs new file mode 100644 index 0000000000..6aa5b2d1e4 --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs @@ -0,0 +1,25 @@ +namespace Volo.Abp.Threading +{ + /// + /// Interface to start/stop self threaded services. + /// + public interface IRunnable + { + /// + /// Starts the service. + /// + void Start(); + + /// + /// Sends stop command to the service. + /// Service may return immediately and stop asynchronously. + /// A client should then call method to ensure it's stopped. + /// + void Stop(); + + /// + /// Waits the service to stop. + /// + void WaitToStop(); + } +} diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs new file mode 100644 index 0000000000..bc72baeddd --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs @@ -0,0 +1,29 @@ +namespace Volo.Abp.Threading +{ + /// + /// Base implementation of . + /// + public abstract class RunnableBase : IRunnable + { + /// + /// A boolean value to check if this is running. + /// + public bool IsRunning => _isRunning; + private volatile bool _isRunning; + + public virtual void Start() + { + _isRunning = true; + } + + public virtual void Stop() + { + _isRunning = false; + } + + public virtual void WaitToStop() + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs new file mode 100644 index 0000000000..91e423b15b --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs @@ -0,0 +1,17 @@ +namespace Volo.Abp.Threading +{ + /// + /// Some extension methods for . + /// + public static class RunnableExtensions + { + /// + /// Calls and then . + /// + public static void StopAndWaitToStop(this IRunnable runnable) + { + runnable.Stop(); + runnable.WaitToStop(); + } + } +} \ No newline at end of file