Skip to content

Instantly share code, notes, and snippets.

@NDiiong
Last active January 22, 2021 06:39
Show Gist options
  • Save NDiiong/e101dbbaec7cdacdc368954e90a4fe1b to your computer and use it in GitHub Desktop.
Save NDiiong/e101dbbaec7cdacdc368954e90a4fe1b to your computer and use it in GitHub Desktop.
AsyncTask.cs
public class AsyncTask : IDisposable
{
private readonly bool _envDTETask;
private readonly object _objlock = new object();
private readonly TaskFunction _taskFunction;
private readonly Thread _thread;
private readonly AutoResetEvent _wakeEvent = new AutoResetEvent(false);
private volatile bool _exiting;
private volatile int _startTime;
private TaskContext _taskContext;
public AsyncTask(TaskFunction task, string name) : this(task, name, dteTask: false)
{
}
public AsyncTask(TaskFunction task, string name, bool dteTask)
{
_taskFunction = task;
_envDTETask = dteTask;
_thread = new Thread(ThreadMain)
{
Name = name
};
if (dteTask)
{
_thread.SetApartmentState(ApartmentState.STA);
}
_thread.Start();
}
public delegate void TaskFunction(TaskContext context);
public void Exit()
{
Cancel();
_exiting = true;
_wakeEvent.Set();
}
public void Start()
{
Start(0);
}
public void Start(int delay)
{
Start(new TaskContext(null), delay);
}
public void Start(TaskContext taskContext)
{
Start(taskContext, 0);
}
public void Start(TaskContext taskContext, int delay)
{
int start_time = Environment.TickCount + delay;
lock (_objlock)
{
bool alreadyStarted = false;
if (_taskContext != null)
{
_taskContext.Cancel();
alreadyStarted = true;
}
_taskContext = taskContext;
if (!alreadyStarted || start_time > _startTime)
{
_startTime = start_time;
}
_wakeEvent.Set();
}
}
private void ThreadMain()
{
while (!_exiting)
{
int start_time;
TaskContext taskContext;
lock (_objlock)
{
start_time = _startTime;
taskContext = _taskContext;
}
while ((start_time == 0 || start_time > Environment.TickCount) && !_exiting)
{
int sleep_time = ((start_time != 0) ? Math.Max(0, start_time - Environment.TickCount) : (-1));
_wakeEvent.WaitOne(sleep_time);
lock (_objlock)
{
start_time = _startTime;
taskContext = _taskContext;
}
}
if (_exiting || taskContext == null)
{
continue;
}
if (_envDTETask)
{
using (new MessageFilter())
{
_taskFunction(taskContext);
}
}
else
{
_taskFunction(taskContext);
}
lock (_objlock)
{
if (!taskContext.Cancelled)
{
_taskContext = null;
_startTime = 0;
}
}
}
}
public void Cancel()
{
lock (_objlock)
{
if (_taskContext != null)
{
_taskContext.Cancel();
_taskContext = null;
_startTime = 0;
}
}
}
public void Dispose()
{
_wakeEvent.Dispose();
}
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000016-0000-0000-C000-000000000046")]
[ComImport]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
[SuppressMessage("Style", "IDE1006", Justification = "<Pending>")]
public class MessageFilter : MarshalByRefObject, IDisposable, IOleMessageFilter
{
private const int HANDLED = 0, RETRYALLOWED = 2, RETRY = 150, CANCEL = -1, WAITANDDISPATCH = 2;
private readonly IOleMessageFilter _oldFilter;
[DllImport("ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter lpMessageFilter, out IOleMessageFilter lplpMessageFilter);
public MessageFilter()
{
CoRegisterMessageFilter(this, out _oldFilter);
}
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr threadIdCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return HANDLED;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr threadIDCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RETRYALLOWED ? RETRY : CANCEL;
}
int IOleMessageFilter.MessagePending(IntPtr threadIDCallee, int dwTickCount, int dwPendingType)
{
return WAITANDDISPATCH;
}
public void Dispose()
{
CoRegisterMessageFilter(_oldFilter, out var _);
GC.SuppressFinalize(this);
}
}
public class TaskContext
{
private volatile bool _cancelled;
public object Arg { get; }
public bool Cancelled => _cancelled;
public TaskContext()
{
}
public TaskContext(object arg)
{
Arg = arg;
}
public void Cancel()
{
_cancelled = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment