I am writing a portable class library that will be used by WPF, Windows Phone, and possibly WinRT applications, and I am doing some work on background threads that sometimes need to be called back to the user interface. I am instantiating classes that do this in the user interface thread, so I can easily save the SynchronizationContext and use it to return to the user interface.
However, in PCL, SynchronizationContext.Send () is deprecated since it is not supported by WinRT and SynchronizationContext.Post () (which runs asynchronously) is not always appropriate.
I decided that I would just wait until the delegate passed to Post () was running, but all my attempts ended in deadlock if Post () was called from the same thread referenced by the saved SynchronizationContext.
Now I managed to fix this by checking if it is the same thread and just calling my delegate if there is one, but the checks are incredibly ugly, including reflecting the values โโof the private API fields, so I thought someone could help me find more the right way.
Here is my current code if you want to see a little:
public static void InvokeSynchronized( this SynchronizationContext context, SendOrPostCallback d, object state )
{
if ( !context.Match( SynchronizationContext.Current ) )
{
ManualResetEvent waitHandle = new ManualResetEvent( false );
Exception error = null;
context.Post( ( o ) =>
{
try
{
d( o );
}
catch ( Exception exc )
{
error = exc;
}
finally
{
waitHandle.Set();
}
},
state );
waitHandle.WaitOne();
if ( error != null )
{
throw error;
}
}
else
{
d( state );
}
}
public static bool Match(this SynchronizationContext sc1, SynchronizationContext sc2)
{
if ( sc2 == null )
{
return false;
}
else if ( sc1 == sc2 || sc1.Equals(sc2) )
{
return true;
}
return sc1.FindManagedThreadId() == sc2.FindManagedThreadId();
}
public static int FindManagedThreadId(this SynchronizationContext sc)
{
try
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
switch ( sc.GetType().FullName )
{
case "System.Windows.Threading.DispatcherSynchronizationContext":
var _dispatcher_field = sc.GetType().GetField( "_dispatcher", bindFlags );
var _dispatcher_value = _dispatcher_field.GetValue( sc );
var Thread_property = _dispatcher_value.GetType().GetProperty( "Thread", bindFlags );
var Thread_value = Thread_property.GetValue( _dispatcher_value, null ) as Thread;
return Thread_value.ManagedThreadId;
}
}
catch ( Exception e )
{
throw new InvalidOperationException( "ManagedThreadId could not be obtained for SynchronizationContext of type " + sc.GetType().FullName, e );
}
throw new InvalidOperationException( "ManagedThreadId not found for SynchronizationContext of type " + sc.GetType().FullName );
}
Thank!
Kรกge source
share