Having
public class ObjFromOtherAppDomain : MarshalByRefObject { public async void Do(MarshalableCompletionSource<bool> source) { await Task.Delay(1000); source.SetResult(true); } } public class MarshalableCompletionSource<T> : MarshalByRefObject { private readonly TaskCompletionSource<T> tsc = new TaskCompletionSource<T>(); public void SetResult(T result) => tsc.SetResult(result); public void SetException(Exception[] exception) => tsc.SetException(exception); public void SetCanceled() => tsc.SetCanceled(); public Task<T> Task => tsc.Task; } Doing
- Create new
AppDomain - Create an instance of
ObjFromOtherAppDomainwithin the newAppDomain - invoke
Domethod passingMarshalableCompletionSourcein order later to know whenasyncDomethod is completed. - Once
Domethod is completed, trying toUnloadtheAppDomain
public static async Task Main() { var otherDomain = AppDomain.CreateDomain("other domain"); var objFromOtherAppDomain = (ObjFromOtherAppDomain)otherDomain .CreateInstanceAndUnwrap( typeof(ObjFromOtherAppDomain).Assembly.FullName, typeof(ObjFromOtherAppDomain).FullName); var source = new MarshalableCompletionSource<bool>(); objFromOtherAppDomain.Do(source); await source.Task; //await Task.Yield(); AppDomain.Unload(otherDomain); } Getting
System.Threading.ThreadAbortException: 'Thread has aborted. (Exception from HRESULT: 0x80131530) exception
Fix
Uncomment await Task.Yield(); line and Unload works well.
Short analysis
Main thread enters Do method and on the line await Task.Delay(1000), Main thread returns back to Main method, while new background thread gets pulled from ThreadPool (it's happening in otherDomain) and continues execution of continuation, in this case, rest of the Do method.
After that, the same (background) thread starts executing rest of the Main method (the part after await source.Task)
At that moment background thread hits AppDomain.Unload(otherDomain), it should be done in otherDomain and happily unload it, but, apparently, it's not.
If i'll yield (release, set free) that background thread by await Task.Yield(), new Background thread comes into play and does AppDomain.Unload happily.
Why is that?
async voidis only meant for event handlers. It results in a fire-and-forget task that can't be awaited. That task may still be running when the application or ... application domain that launched it terminates. Useasync Taskif you want an asynchronous function that doesn't return anything. If you don't want the application to terminate before the task does, you'll have to wait for itasync Task Main()and inside it call anasync voidmethod that waits eg for 10 seconds before trying to access a global object.async voidis there to support event handlers, but you can't really useTaskhere instead sinceTaskisn't serializable and can't be used cross app-domain. That's why I'm passingMarshalableCompletionSourcetoDomethod to Wait for its completion.async Taskorasync ValueTask. That's the invariant. What are you trying to do in the first place? There are probably other, better ways to do it. If you want two whatevers to communicate, you can use Channels, similar to Go's channels