using System;
using System.ComponentModel;
using System.Runtime.Remoting.Contexts;
using System.Threading;
using log4net;
using PostSharp.Aspects;
namespace DelftTools.Utils.Aop
{
///
/// Implements thread-safe calls for Windows.Forms methods.
///
[Serializable]
[Synchronization]
public class InvokeRequiredAttribute : MethodInterceptionAspect
{
private static readonly ILog log = LogManager.GetLogger(typeof(InvokeRequiredAttribute));
private static int pendingInvokes; //number of invokes pending (global!)
private static readonly object InvokeLock = new object(); //object to lock on
private static readonly ManualResetEvent ZeroPendingInvokes = new ManualResetEvent(true); //true/signalled if there are no invokes pending
///
/// Call this method with a using statement around the content of the Dispose method you want to protected.
///
///
public static IDisposable BlockInvokeCallsDuringDispose()
{
return new InvokeBlocker();
}
public override sealed void OnInvoke(MethodInterceptionArgs args)
{
var synchronizeObject = InvokeRequiredInfo.SynchronizeObject ?? args.Instance as ISynchronizeInvoke;
if (synchronizeObject == null || !synchronizeObject.InvokeRequired)
{
args.Proceed();
}
else
{
lock (InvokeLock)
{
try
{
if (Interlocked.Increment(ref pendingInvokes) > 0)
{
ZeroPendingInvokes.Reset();
}
synchronizeObject.Invoke(new Action(args.Proceed), new object[0]);
}
catch (ObjectDisposedException e)
{
LogInvokeError(args.Method.Name, e, false);
}
finally
{
if (Interlocked.Decrement(ref pendingInvokes) == 0)
{
ZeroPendingInvokes.Set();
}
}
}
}
}
private static void LogInvokeError(string methodName, Exception e, bool beforeCall)
{
log.Error(string.Format("Thread synchronization error (call {1}): {0}", methodName,
beforeCall ? "skipping" : "aborted"), e);
}
private class InvokeBlocker : IDisposable
{
private readonly bool hasLock;
public InvokeBlocker()
{
int maxWaits = 15;
do
{
//while there are invokes pending
while (!ZeroPendingInvokes.WaitOne(0))
{
//block (call Application.DoEvents), so that other threads can finish their invokes
InvokeRequiredInfo.WaitMethod();
if (maxWaits-- <= 0) // prevent deadlocks
{
return; //hasLock = false
}
}
} while (!Monitor.TryEnter(InvokeLock)); //try to grab the invoke lock (to prevent additional invokes from occuring)
hasLock = true;
}
public void Dispose()
{
//give up the lock; any invokes waiting for this lock are processed after this; but if the object is
//now correctly disposed, they are skipped
if (hasLock)
{
Monitor.Exit(InvokeLock);
}
}
}
}
//In seperate class to make sure you don't need a postsharp reference to fill in
public static class InvokeRequiredInfo
{
public static Action WaitMethod;
//to be fill in by application:
public static ISynchronizeInvoke SynchronizeObject { get; set; }
// end fill in
}
}