/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Threading;
namespace Amazon.Runtime.Internal.Util
{
///
/// Class to perform actions on a background thread.
/// Uses a single background thread and performs actions
/// on it in the order the data was sent through the instance.
///
internal class BackgroundDispatcher : IDisposable
{
#region Properties
private bool isDisposed = false;
private Action action;
private Queue queue;
private Thread backgroundThread;
private AutoResetEvent resetEvent;
private bool shouldStop;
public bool IsRunning { get; private set; }
#endregion
#region Constructor/destructor
public BackgroundDispatcher(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
queue = new Queue();
resetEvent = new AutoResetEvent(false);
shouldStop = false;
this.action = action;
backgroundThread = new Thread(Run);
backgroundThread.IsBackground = true;
backgroundThread.Start();
}
~BackgroundDispatcher()
{
Stop();
this.Dispose(false);
}
#endregion
#region Dispose Pattern Implementation
///
/// Implements the Dispose pattern
///
/// Whether this object is being disposed via a call to Dispose
/// or garbage collected.
protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
if (disposing && resetEvent != null)
{
#if NETSTANDARD
resetEvent.Dispose();
#else
resetEvent.Close();
#endif
resetEvent = null;
}
this.isDisposed = true;
}
}
///
/// Disposes of all managed and unmanaged resources.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Public Methods
const int MAX_QUEUE_SIZE = 100;
public void Dispatch(T data)
{
if (queue.Count < MAX_QUEUE_SIZE)
{
lock (queue)
{
if (queue.Count < MAX_QUEUE_SIZE)
queue.Enqueue(data);
}
}
resetEvent.Set();
}
public void Stop()
{
shouldStop = true;
resetEvent.Set();
}
#endregion
#region Private methods
private void Run()
{
IsRunning = true;
while (!shouldStop)
{
HandleInvoked();
resetEvent.WaitOne();
}
HandleInvoked();
IsRunning = false;
}
private void HandleInvoked()
{
while (true)
{
bool dataPresent = false;
T data = default(T);
lock (queue)
{
if (queue.Count > 0)
{
data = queue.Dequeue();
dataPresent = true;
}
}
if (!dataPresent)
break;
try
{
action(data);
}
catch { }
}
}
#endregion
}
///
/// Class to invoke actions on a background thread.
/// Uses a single background thread and invokes actions
/// on it in the order they were invoked through the instance.
///
internal class BackgroundInvoker : BackgroundDispatcher
{
public BackgroundInvoker()
: base(action => action())
{
}
}
}