Why does the Task.ContinueWith fail to execute in this Unit Test? - c#

I have come across a problem with a unit test that failed because a TPL Task never executed its ContinueWith(x, TaskScheduler.FromCurrentSynchronizationContext()).
The problem turned out to be because a Winforms UI Control was accidentally being created before the Task was started.
Here is an example that reproduces it. You will see that if you run the test as-is, it passes. If you run the test with the Form line uncommented, it fails.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Create new sync context for unit test
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var waitHandle = new ManualResetEvent(false);
var doer = new DoSomethinger();
//Uncommenting this line causes the ContinueWith part of the Task
//below never to execute.
//var f = new Form();
doer.DoSomethingAsync(() => waitHandle.Set());
Assert.IsTrue(waitHandle.WaitOne(10000), "Wait timeout exceeded.");
}
}
public class DoSomethinger
{
public void DoSomethingAsync(Action onCompleted)
{
var task = Task.Factory.StartNew(() => Thread.Sleep(1000));
task.ContinueWith(t =>
{
if (onCompleted != null)
onCompleted();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Can anyone explain why this is the case?
I thought it might have been because the wrong SynchronizationContext is used, but actually, the ContinueWith never executes at all! And besides, in this unit test, whether or not it is the correct SynchronizationContext is irrelevant because as long as the waitHandle.set() is called on any thread, the test should pass.

I overlooked the comments section in your code, Indeed that fails when uncommenting the var f = new Form();
Reason is subtle, Control class will automatically overwrite the synchronization context to WindowsFormsSynchronizationContext if it sees that SynchronizationContext.Current is null or its is of type System.Threading.SynchronizationContext.
As soon as Control class overwrite the SynchronizationContext.Current with WindowsFormsSynchronizationContext, all the calls to Send and Post expects the windows message loop to be running in order to work. That's not going to happen till you created the Handle and you run a message loop.
Relevant part of the problematic code:
internal Control(bool autoInstallSyncContext)
{
...
if (autoInstallSyncContext)
{
//This overwrites your SynchronizationContext
WindowsFormsSynchronizationContext.InstallIfNeeded();
}
}
You can refer the source of WindowsFormsSynchronizationContext.InstallIfNeeded here.
If you want to overwrite the SynchronizationContext, you need your custom implementation of SynchronizationContext to make it work.
Workaround:
internal class MyContext : SynchronizationContext
{
}
[TestMethod]
public void TestMethod1()
{
// Create new sync context for unit test
SynchronizationContext.SetSynchronizationContext(new MyContext());
var waitHandle = new ManualResetEvent(false);
var doer = new DoSomethinger();
var f = new Form();
doer.DoSomethingAsync(() => waitHandle.Set());
Assert.IsTrue(waitHandle.WaitOne(10000), "Wait timeout exceeded.");
}
Above code works as expected :)
Alternatively you could set WindowsFormsSynchronizationContext.AutoInstall to false, that will prevent automatic overwriting of the synchronization context mentioned above.(Thanks for OP #OffHeGoes for mentioning this in comments)

With the line commented out, your SynchronizationContext is the default one you created. This will cause TaskScheduler.FromCurrentSynchrozisationContext() to use the default scheduler, which will run the continuation on the thread pool.
Once you create a Winforms object like your Form, the current SynchronizationContext becomes a WindowsFormsSynchronizationContext, which in turn will return a scheduler that depends on the WinForms message pump to schedule the continuation.
Since there is no WinForms pump in a unit test, the continuation never gets run.

Related

Forcing certain code to always run on the same thread

We have an old 3rd party system (let's call it Junksoft® 95) that we interface with via PowerShell (it exposes a COM object) and I'm in the process of wrapping it in a REST API (ASP.NET Framework 4.8 and WebAPI 2). I use the System.Management.Automation nuget package to create a PowerShell in which I instantiate Junksoft's COM API as a dynamic object that I then use:
//I'm omitting some exception handling and maintenance code for brevity
powerShell = System.Management.Automation.PowerShell.Create();
powerShell.AddScript("Add-Type -Path C:\Path\To\Junksoft\Scripting.dll");
powerShell.AddScript("New-Object Com.Junksoft.Scripting.ScriptingObject");
dynamic junksoftAPI = powerShell.Invoke()[0];
//Now we issue commands to junksoftAPI like this:
junksoftAPI.Login(user,pass);
int age = junksoftAPI.GetAgeByCustomerId(custId);
List<string> names = junksoftAPI.GetNames();
This works fine when I run all of this on the same thread (e.g. in a console application). However, for some reason this usually doesn't work when I put junksoftAPI into a System.Web.Caching.Cache and use it from different controllers in my web app. I say ususally because this actually works when ASP.NET happens to give the incoming call to the thread that junksoftAPI was created on. If it doesn't, Junksoft 95 gives me an error.
Is there any way for me to make sure that all interactions with junksoftAPI happen on the same thread?
Note that I don't want to turn the whole web application into a single-threaded application! The logic in the controllers and elswhere should happen like normal on different threads. It should only be the Junksoft interactions that happen on the Junksoft-specific thread, something like this:
[HttpGet]
public IHttpActionResult GetAge(...)
{
//finding customer ID in database...
...
int custAge = await Task.Run(() => {
//this should happen on the Junksoft-specific thread and not the next available thread
var cache = new System.Web.Caching.Cache();
var junksoftAPI = cache.Get(...); //This has previously been added to cache on the Junksoft-specific thread
return junksoftAPI.GetAgeByCustomerId(custId);
});
//prepare a response using custAge...
}
You can create your own singleton worker thread to achieve this. Here is the code which you can plug it into your web application.
public class JunkSoftRunner
{
private static JunkSoftRunner _instance;
//singleton pattern to restrict all the actions to be executed on a single thread only.
public static JunkSoftRunner Instance => _instance ?? (_instance = new JunkSoftRunner());
private readonly SemaphoreSlim _semaphore;
private readonly AutoResetEvent _newTaskRunSignal;
private TaskCompletionSource<object> _taskCompletionSource;
private Func<object> _func;
private JunkSoftRunner()
{
_semaphore = new SemaphoreSlim(1, 1);
_newTaskRunSignal = new AutoResetEvent(false);
var contextThread = new Thread(ThreadLooper)
{
Priority = ThreadPriority.Highest
};
contextThread.Start();
}
private void ThreadLooper()
{
while (true)
{
//wait till the next task signal is received.
_newTaskRunSignal.WaitOne();
//next task execution signal is received.
try
{
//try execute the task and get the result
var result = _func.Invoke();
//task executed successfully, set the result
_taskCompletionSource.SetResult(result);
}
catch (Exception ex)
{
//task execution threw an exception, set the exception and continue with the looper
_taskCompletionSource.SetException(ex);
}
}
}
public async Task<TResult> Run<TResult>(Func<TResult> func, CancellationToken cancellationToken = default(CancellationToken))
{
//allows only one thread to run at a time.
await _semaphore.WaitAsync(cancellationToken);
//thread has acquired the semaphore and entered
try
{
//create new task completion source to wait for func to get executed on the context thread
_taskCompletionSource = new TaskCompletionSource<object>();
//set the function to be executed by the context thread
_func = () => func();
//signal the waiting context thread that it is time to execute the task
_newTaskRunSignal.Set();
//wait and return the result till the task execution is finished on the context/looper thread.
return (TResult)await _taskCompletionSource.Task;
}
finally
{
//release the semaphore to allow other threads to acquire it.
_semaphore.Release();
}
}
}
Console Main Method for testing:
public class Program
{
//testing the junk soft runner
public static void Main()
{
//get the singleton instance
var softRunner = JunkSoftRunner.Instance;
//simulate web request on different threads
for (var i = 0; i < 10; i++)
{
var taskIndex = i;
//launch a web request on a new thread.
Task.Run(async () =>
{
Console.WriteLine($"Task{taskIndex} (ThreadID:'{Thread.CurrentThread.ManagedThreadId})' Launched");
return await softRunner.Run(() =>
{
Console.WriteLine($"->Task{taskIndex} Completed On '{Thread.CurrentThread.ManagedThreadId}' thread.");
return taskIndex;
});
});
}
}
}
Output:
Notice that, though the function was launched from the different threads, some portion of code got always executed always on the same context thread with ID: '5'.
But beware that, though all the web requests are executed on independent threads, they will eventually wait for some tasks to get executed on the singleton worker thread. This will eventually create a bottle neck in your web application. This is anyway your design limitation.
Here is how you could issue commands to the Junksoft API from a dedicated STA thread, using a BlockingCollection class:
public class JunksoftSTA : IDisposable
{
private readonly BlockingCollection<Action<Lazy<dynamic>>> _pump;
private readonly Thread _thread;
public JunksoftSTA()
{
_pump = new BlockingCollection<Action<Lazy<dynamic>>>();
_thread = new Thread(() =>
{
var lazyApi = new Lazy<dynamic>(() =>
{
var powerShell = System.Management.Automation.PowerShell.Create();
powerShell.AddScript("Add-Type -Path C:\Path\To\Junksoft.dll");
powerShell.AddScript("New-Object Com.Junksoft.ScriptingObject");
dynamic junksoftAPI = powerShell.Invoke()[0];
return junksoftAPI;
});
foreach (var action in _pump.GetConsumingEnumerable())
{
action(lazyApi);
}
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
}
public Task<T> CallAsync<T>(Func<dynamic, T> function)
{
var tcs = new TaskCompletionSource<T>(
TaskCreationOptions.RunContinuationsAsynchronously);
_pump.Add(lazyApi =>
{
try
{
var result = function(lazyApi.Value);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
}
public Task CallAsync(Action<dynamic> action)
{
return CallAsync<object>(api => { action(api); return null; });
}
public void Dispose() => _pump.CompleteAdding();
public void Join() => _thread.Join();
}
The purpose of using the Lazy class is for surfacing a possible exception during the construction of the dynamic object, by propagating it to the callers.
...exceptions are cached. That is, if the factory method throws an exception the first time a thread tries to access the Value property of the Lazy<T> object, the same exception is thrown on every subsequent attempt.
Usage example:
// A static field stored somewhere
public static readonly JunksoftSTA JunksoftStatic = new JunksoftSTA();
await JunksoftStatic.CallAsync(api => { api.Login("x", "y"); });
int age = await JunksoftStatic.CallAsync(api => api.GetAgeByCustomerId(custId));
In case you find that a single STA thread is not enough to serve all the requests in a timely manner, you could add more STA threads, all of them running the same code (private readonly Thread[] _threads; etc). The BlockingCollection class is thread-safe and can be consumed concurrently by any number of threads.
If you did not say that was a 3rd party tool, I would have asumed it is a GUI class. For practical reasons, it is a very bad idea to have multiple threads write to them. .NET enforces a strict "only the creating thread shall write" rule, from 2.0 onward.
WebServers in general and ASP.Net in particular use a pretty big thread pool. We are talking 10's to 100's of Threads per Core. That means it is really hard to nail any request down to a specific Thread. You might as well not try.
Again, looking at the GUI classes might be your best bet. You could basically make a single thread with the sole purpose of immitating a GUI's Event Queue. The Main/UI Thread of your average Windows Forms application, is responsible for creating every GUI class instance. It is kept alive by polling/processing the event queue. It ends onlyx when it receies a cancel command, via teh Event Queue. Dispatching just puts orders into that Queue, so we can avoid Cross-Threading issues.

What's the difference between Foo().Result and Task.Run(() => Foo()).Result in C#?

In C# what is the difference between these two statements? If I use the first one in my constructor in my test classes I get a deadlock, or something similar, and the tests never finish. With the second one the code works.
// Deadlock.
var r = MyMethod().Result;
// Works.
var r = Task.Run(() => MyMethod()).Result;
Update: There is a bit more context in this commit: https://github.com/webCRMdotcom/erp-integrations/pull/92/commits/dd8af89899ce1de837ef6e34f0688a685a5cea3b.
The difference is the starting thread context.
Here a simple sample
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
string r;
OutputThreadInfo("Main");
r = MyMethod().Result;
r = Task.Run( () => MyMethod() ).Result;
}
public static async Task<string> MyMethod()
{
OutputThreadInfo("MyMethod");
await Task.Delay(50);
return "finished";
}
private static void OutputThreadInfo(string context)
{
Console.WriteLine("{0} {1}",context,System.Threading.Thread.CurrentThread.ManagedThreadId);
}
}
.net fiddle
which will output
Main 32
MyMethod 32
MyMethod 63
The first call of MyMethod will start at the same thread as Main and if started from a thread with a synchronization context it will block.
The second call of MyMethod will start from a different thread (worker thread from thread pool) as Main which does not have a synchronization context and therefor will not block.
PS You should keep in mind that Console applications do not have a synchronization context as default but WinForms, WPF, UWP application do have and so will behave somehow different on async/await
Task.Result and Task.Wait block the current thread you should use await for this to work without any problems. (Though they only block if not already completed).
The second line will create a task and will start it's execution on a available thread in the Thread Pool and that's why it doesn't block.
This is because the Task construct when used with async-await will generate a State Machine that keeps track of all the awaits used in the code block and when all finishes then it can return the result. Keep in mind thought that depending on the Synchronization Context you are in, the code after await may run on a different thread then the one the task started.
So what I do when I have to execute synchronous an async method I use a small piece of code like this:
private static readonly TaskFactory _tf = new TaskFactory(
CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _tf.StartNew<Task<TResult>>((Func<Task<TResult>>) (() =>
{
return func();
})).Unwrap<TResult>().GetAwaiter().GetResult();
}
Keep in mind that, if needed, you have to use the same CultureInfo inside the RunSync StarNew task factory call so you won't have this kind of problems.

"Thread was being aborted." in test that tests code that fires a delegate on a background thread

I have some old code that I'm trying to writes tests for. the code parses a log file (on a background thread) and when finished fires off a passed in delegate.
i.e.
public delegate void finread(LogData l, LRParseState l, string e="");
void Thread_ParseLog(object info) {
var info = ti as ThreadInfo;
// some time later
info.fin(log, state, error);
}
public static void ParseErrorLog(string log, finread fin){
var pts = new ParameterizedThreadStart(Thread_ParseLog);
new Thread(pts).Start(new ThreadInfo(log, fin));
}
The code is production code and every thing works ok and has done for a long time, but when I try and test it I get the "Thread was being aborted." exception raised in the Thread_ParseLog method.
The test looks like this:
void llt(string name, Action<LogData, LRParseState> test) {
finread d = (LogData l, LRParseState s, string e) => {
test(l, s);
};
LogReader.ParseErrorLog(name, d);
}
[TestMethod]
public void Create_LogReader_Big_Log() {
llt(ERROR_LOG, (log, state) => {
Assert.IsTrue(log != null); // never get here!
});
}
The test data is largeish, about 55mb, which takes about 500ms to process normally.
I'm also getting errors in the output window:
Exception thrown: 'System.Threading.ThreadAbortException' in
mscorlib.dll System.AppDomainUnloadedException: Attempted to access an
unloaded AppDomain. This can happen if the test(s) started a thread
but did not stop it. Make sure that all the threads started by the
test(s) are stopped before completion.
Which seem to point at some kind of thread sync problems, but there's nothing i can do about the code i'm testing.
It's, obviously, the way I've written my test and I can fix it by changing my test but I'm not sure why its happening.
TIA.
Use synchronization mechanisms like for example a ManualResetEvent to wait for the asynchronous parts of the test to finish before leaving the test method.
[TestMethod]
public void Create_LogReader_Big_Log() {
// Use this event to wait until the asynchronous code has been executed
// before leaving the test method
ManualResetEvent resetEvent = new ManualResetEvent(false);
LogData logDataReceived = null;
llt(ERROR_LOG, (log, state) => {
logDataReceived = log;
// Signal that the test has reached the end
resetEvent.Set();
});
// Wait for the event to be set
resetEvent.WaitOne();
// Additionally wait for a grace period to allow the other thread to fully terminate
Thread.Sleep(500);
// Now perform the asserts on the received data
Assert.IsTrue(logDataReceived != null);
}
Use an async test method and give it a small delay to run.
[TestMethod]
public async Task Create_LogReader_Big_Log()
{
llt(ERROR_LOG, (log, state) => {
Assert.IsTrue(log != null); // never get here!
});
await Task.Delay(3000);
}

Dispatcher.BeginInvoke Method 'freezing' after second execution

I'm making a windows phone game with Unity3d and I have the need to call a method from the Unity thread asynchronously from the UI thread.
This all works, however with one particular method the first execution executes as expected however after the second it seems to lock up the game.
private async static Task<String> ShowDescriptionProductListing()
{
var x = await CurrentApp.LoadListingInformationAsync();
StringBuilder builder = new StringBuilder();
builder.AppendFormat("{0}\n{1}", x.Description,
x.ProductListings.FirstOrDefault().Value);
return builder.ToString();
}
public static void ShowDescrProduct()
{
string x = ShowDescriptionProductListing().Result;
MessageBox.Show(x);
}
I think the line:
var x = await CurrentApp.LoadListingInformationAsync();
Is most likely the culprit, however I'm having a hard time debugging it.
The class which 'holds' that method in unity is like so:
public static class HelperClass
{
public static void ShowDescrProduct()
{
Dispatcherr.InvokeOnUIThread(Tests.ShowDescrProduct); //The method above
}
}
Dispatcherr (Yeah i need to use namespaces haha) just holds two Action properties that I set inside the UI thread.
public void EnterUIThread(Action action)
{
Dispatcher.BeginInvoke(() =>
{
action();
});
}
private void Unity_Loaded()
{
Dispatcherr.InvokeUIThread = EnterUIThread; //One of the actions I just
//mentioned being assigned the above
//method
}
And it's in the EnterUIThread call to Dispatcher.BeginInvoke that it seems to get locked up, only after the first call - which is always successful.
Confusing me slightly to say the least.
Anyone able to give any insight?
Thanks in advance
You're calling Result on the asynchronous operation. This is going to cause the UI thread to block until the asynchronous operation finishes. The asynchronous operation needs to wait for the UI thread to be free so that the continuation to LoadListingInformationAsync can be scheduled in the UI thread.
Both operations are waiting on each other to finish. Deadlock.
You need to not block the UI thread while waiting for this operation to finish. You should await it instead, making ShowDescrProduct and async method.

Unit testing code that uses Task.Factory.StartNew().ContinueWith()

so I have some code
Task.Factory.StartNew(() => this.listener.Start()).ContinueWith(
(task) =>
{
if (task.IsCompleted)
{
this.status = WorkerStatus.Started;
this.RaiseStatusChanged();
this.LogInformationMessage("Worker Started.");
}
});
When I am testing I am mocking all the dependant objects (namley this.listener.Start()). the problem is that the test finishes executing before ContinueWith can be called. When I debug it gets called fine due to the extra delay of me stepping through code.
so how can I - from the test code in a different assembly - ensure that the code is run before my test hits its asserts?
I could just use Thread.Sleep ... but this seems like a really hacky way of doing it.
I guess I am looking for the Task version of Thread.Join.
Consider the following:
public class SomeClass
{
public void Foo()
{
var a = new Random().Next();
}
}
public class MyUnitTest
{
public void MyTestMethod()
{
var target = new SomeClass();
target.Foo(); // What to assert, what is the result?..
}
}
What is the value assigned to a? You cannot tell, unless the result is returned outside the method Foo() (as the return value, a public property, an event, etc.).
The process of "coordinating the actions of threads for a predictable outcome" is called Synchronization.
One of the easiest solutions in your case might be to return the instance of Task class and the use its Wait() method:
var task = Task.Factory.StartNew(() => Method1())
.ContinueWith(() => Method2());
No need to wait for the first task, because ContinueWith() creates a continuation that executes asynchronously when the target Task completes (MSDN):
task.Wait();
I don't think there is an easy-yet-practical way of doing this. Ran into the same problem myself just now and Thread.Sleep(X) is by far the simplest (if not elegant) way of getting around the problem.
The only other solution that I considered is hiding the Task.Factory.StartNew() call behind an interface that you can mock from your test thus removing the actual execution of the task entirely in the test scenario (but still have an expectation that the interface method will be called. For example:
public interface ITaskWrapper
{
void TaskMethod();
}
And your concrete implementation:
public class MyTask : ITaskWrapper
{
public void TaskMethod()
{
Task.Factory.StartNew(() => DoSomeWork());
}
}
Then just mock ITaskWrapper in your test method and set an expectation on TaskMethod being called.
If there's any way for you to be notified of when the processing has ended (can you add a handler for that StatusChanged event?), use a ManualResetEvent and wait on it with a reasonable timeout. If the timeout expired fail the test, otherwise go on and perform your assertions.
E.g.
var waitHandle = new ManualResetEvent(false);
sut.StatusChanged += (s, e) => waitHandle.Set();
sut.DoStuff();
Assert.IsTrue(waitHandle.WaitOne(someTimeout), "timeout expired");
// do asserts here
The continuation task will still run regardless of whether the initial task completed before the ContinueWith() call or not. I double checked this with the following:
// Task immediately exits
var task = Task.Factory.StartNew(() => { });
Thread.Sleep(100);
// Continuation on already-completed task
task.ContinueWith(t => { MessageBox.Show("!"); });
Debug further. Maybe your task is failing.
When dealing with asynchronous processes during code under test that use Reactive Extensions, one approach is to use a TestScheduler. The TestScheduler can be moved forward in time, drained of all shceduled tasks, etc. So your code under test can take an IScheduler, which you provide a TestScheduler instance for. Then your test can manipulate time without needing to actually sleep, wait or synchronize. An improvement on this approach is Lee Campbell's ISchedulerProvider approach.
If you use Observable.Start instead of Task.Factory.StartNew in your code, you can then use your TestScheduler in the unit test to push through all the scheduled tasks.
For example, your code under test could look something like this:
//Task.Factory.StartNew(() => DoSomething())
// .ContinueWith(t => DoSomethingElse())
Observable.Start(() => DoSomething(), schedulerProvider.ThreadPool)
.ToTask()
.ContinueWith(t => DoSomethingElse())
and in your unit test:
// ... test code to execute the code under test
// run the tasks on the ThreadPool scheduler
testSchedulers.ThreadPool.Start();
// assertion code can now run

Categories