I've tried various methods from other SO questions over the last week and this is the best approach I found yet, but I do not know how to unit test this with NUnit.
private void VerifyDispatcherThread()
{
var context = SynchronizationContext.Current;
if (context != null && context is DispatcherSynchronizationContext)
{
Log.For(this).Info("WARNING! - Method Should Not Be Called From Dispatcher Thread.");
if (DispatcherThreadSafetyEnabled)
{
throw new ThreadStateException("Method Should Not Be Called From Dispatcher Thread.");
}
}
}
HERE IS OUR TEST (Not Working)
[Test]
public void Should_throw_exception_when_called_from_dispatcher()
{
// Arrange
var called = false;
Boolean? success = null;
var httpClient = new HttpClient(HttpTimeOut);
// This emulates WPF dispatcher thread
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
// Act
dispatcher.BeginInvoke(delegate()
{
try
{
ConfigurationManager.AppSettings.Set("DispatcherThreadSafetyEnabled", "true");
httpClient.Post("http://Foo", "content xml");
success = true;
}
catch
{
success = false;
}
finally
{
ConfigurationManager.AppSettings.Set("DispatcherThreadSafetyEnabled", "false");
}
called = true;
});
// this part is a bit ugly
// spinlock for a bit to give the other thread a chance
var timeout = 0;
while (!called && timeout++ < 1000)
Thread.Sleep(1);
// Assert
Assert.IsNotNull(success, "Method was never invoked");
Assert.IsFalse(success.Value, "Expected to fail");
}
DispatcherThreadSafetyEnabled is a app.config setting we have so we can toggle only logging or throwing exceptions.
What we are trying to do is verify the calling method isn't using the GUI thread when invoked. It appears to be working but we are having a hard time trying to unit test this.
Here's hoping someone has done this and can help us out with our challenge.
The current SynchronizationContext is set to different implementations by different frameworks (Winforms, WPF, etc).
Quoting from this MSDN article:
WindowsFormsSynchronizationContext (System.Windows.Forms.dll:
System.Windows.Forms) Windows Forms apps will create and install a
WindowsFormsSynchronizationContext as the current context for any
thread that creates UI controls.
DispatcherSynchronizationContext (WindowsBase.dll:
System.Windows.Threading) WPF and Silverlight applications use a
DispatcherSynchronizationContext, which queues delegates to the UI
thread’s Dispatcher with “Normal” priority. This
SynchronizationContext is installed as the current context when a
thread begins its Dispatcher loop by calling Dispatcher.Run.
Since you are only testing for not null and the type of the SynchronizationContext, try setting current SynchronizationContext to an instance of DispatcherSynchronizationContextby calling SynchronizationContext.SetSynchronizationContext from your unit tests. Sample code below:
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
Please note this is not a recommended in a Winforms/WPF application though.
Related
I need to control a camera from within an ASP.NET core api and all communication is via a pInvoke dll.
In the docs it explicitly states
To create a user thread and access the camera from that thread, be sure to execute CoInitializeEx( NULL,
COINIT_APARTMENTTHREADED ) at the start of the thread and CoUnInitialize() at the end.
e.g
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
CoUninitialize()
My camera service works from a winforms application (STA) however when I move it over to my API, the callback does not fire when events happen.
I've tried wrapping the component in an STA thread and setting up an execution loop but callbacks still do not fire.
I think I might need a message pump but am unsure exactly how this should work.
Non working code:
Thread handlerThread;
handlerThread = new Thread(STAThreadLoop);
handlerThread.SetApartmentState(ApartmentState.STA);
handlerThread.Start();
and in the thread loop
void STAThreadLoop()
{
logger.LogInformation("Starting STAThreadLoop...");
lock (handlerThreadLock)
{
handlerSignal.Set();
while (!exitHandlerThreadLoop)
{
Thread.Yield();
Monitor.Wait(handlerThreadLock);
if (handlerThreadAction != null)
{
try
{
handlerThreadAction();
}
catch (Exception ex)
{
logger.LogError(ex, "Error executing action on STA thread: {ThreadName}", Thread.CurrentThread.Name);
}
}
Monitor.Pulse(handlerThreadLock);
}
}
}
and then to create the component
RunSTAAction(() =>
{
handler = new SDKHandler(loggerFactory.CreateLogger<SDKHandler>());
});
and the method to transition to the STA thread
void RunSTAAction(Action action)
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
lock (handlerThreadLock)
{
handlerThreadAction = action;
Monitor.Pulse(handlerThreadLock);
Monitor.Wait(handlerThreadLock);
}
}
else
{
action();
}
}
Update: This is actually fixed, see answer below
I found a way to do this using the excellent answer by Noseratio in this question: StaTaskScheduler and STA thread message pumping
Effectively, we create an instance of the ThreadAffinityTaskScheduler and pass the WaitHelpers.WaitWithMessageLoop as a wait function.
ThreadAffinityTaskScheduler messageScheduler;
messageScheduler = new ThreadAffinityTaskScheduler(3, staThreads: true, waitHelper: WaitHelpers.WaitWithMessageLoop);
messageScheduler.Run(new Action(STAThreadLoop), CancellationToken.None);
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.
I am using the Impersonator class (see http://www.codeproject.com/KB/cs/zetaimpersonator.aspx) to switch the user context at runtime.
At the same time, i am now restructuring my program from a single threaded design to multi-threaded one (using TPL / mainly the Task type, that is).
As this impersonation is something that is happening with native API functions on a thread level, i was wondering how far TPL is compatible with it. If i change the user context inside a task, is that user context still set if the task is finished and the thread returns to the ThreadPool? Will other tasks started inside this task implicitly use that context?
I tried to find out by myself with unit testing, and my deduction from the first unit test:
Tasks started inside a thread while impersonated "magically" inherit the user context.
The inherited impersonation is not revoked when the origin task/thread does its Impersonation.Undo().
The second unit test shows that if the impersonation is not explicitly revoked, the user context "survives" on the thread returning to the thread pool, and other following tasks may now be randomly run in different user contexts, depending on the thread they are assigned to.
My question: Is there a better way to realize impersonation than via native API calls? Maybe one that is more focused on TPL and bound to a task instead of a thread? If there is a change to mitigate the risk of executing tasks in a random context i would gladly do it...
These are the 2 unit tests i wrote. You will have to modify the code slightly to use your own mechanism for receiving user credentials if you want to run the tests yourself, and the log4net calls are surely easily removed.
Yeah, i know, Thread.Sleep() is bad style, i am guilty of having been lazy there... ;-)
private string RetrieveIdentityUser()
{
var windowsIdentity = WindowsIdentity.GetCurrent();
if (windowsIdentity != null)
{
return windowsIdentity.Name;
}
return null;
}
[TestMethod]
[TestCategory("LocalTest")]
public void ThreadIdentityInheritanceTest()
{
string user;
string pw;
Security.Decode(CredentialsIdentifier, out user, out pw);
string userInMainThread = RetrieveIdentityUser();
string userInTask1BeforeImpersonation = null;
string userInTask1AfterImpersonation = null;
string userInTask2 = null;
string userInTask3 = null;
string userInTask2AfterImpersonationUndo = null;
var threadlock = new object();
lock (threadlock)
{
new Task(
() =>
{
userInTask1BeforeImpersonation = RetrieveIdentityUser();
using (new Impersonator(user, Domain, pw))
{
userInTask1AfterImpersonation = RetrieveIdentityUser();
lock (threadlock)
{
Monitor.Pulse(threadlock);
}
new Task(() =>
{
userInTask2 = RetrieveIdentityUser();
Thread.Sleep(200);
userInTask2AfterImpersonationUndo = RetrieveIdentityUser();
}).Start();
Thread.Sleep(100);
}
}).Start();
Monitor.Wait(threadlock);
RetrieveIdentityUser();
new Task(() => { userInTask3 = RetrieveIdentityUser(); }).Start();
Thread.Sleep(300);
Assert.IsNotNull(userInMainThread);
Assert.IsNotNull(userInTask1BeforeImpersonation);
Assert.IsNotNull(userInTask1AfterImpersonation);
Assert.IsNotNull(userInTask2);
Assert.IsNotNull(userInTask3);
// context in both threads equal before impersonation
Assert.AreEqual(userInMainThread, userInTask1BeforeImpersonation);
// context has changed in task1
Assert.AreNotEqual(userInTask1BeforeImpersonation, userInTask1AfterImpersonation);
// impersonation to the expected user
Assert.AreEqual(Domain + "\\" + user, userInTask1AfterImpersonation);
// impersonation is inherited
Assert.AreEqual(userInTask1AfterImpersonation, userInTask2);
// a newly started task from the main thread still shows original user context
Assert.AreEqual(userInMainThread, userInTask3);
// inherited impersonation is not revoked
Assert.AreEqual(userInTask2, userInTask2AfterImpersonationUndo);
}
}
[TestMethod]
[TestCategory("LocalTest")]
public void TaskImpersonationTest()
{
int tasksToRun = 100; // must be more than the minimum thread count in ThreadPool
string userInMainThread = RetrieveIdentityUser();
var countdownEvent = new CountdownEvent(tasksToRun);
var exceptions = new List<Exception>();
object threadLock = new object();
string user;
string pw;
Security.Decode(CredentialsIdentifier, out user, out pw);
for (int i = 0; i < tasksToRun; i++)
{
new Task(() =>
{
try
{
try
{
Logger.DebugFormat("Executing task {0} on thread {1}...", Task.CurrentId, Thread.CurrentThread.GetHashCode());
Assert.AreEqual(userInMainThread, RetrieveIdentityUser());
//explicitly not disposing impersonator / reverting impersonation
//to see if a thread reused by TPL has its user context reset
// ReSharper disable once UnusedVariable
var impersonator = new Impersonator(user, Domain, pw);
Assert.AreEqual(Domain + "\\" + user, RetrieveIdentityUser());
}
catch (Exception e)
{
lock (threadLock)
{
var newException = new Exception(string.Format("Task {0} on Thread {1}: {2}", Task.CurrentId, Thread.CurrentThread.GetHashCode(), e.Message));
exceptions.Add(newException);
Logger.Error(newException);
}
}
}
finally
{
countdownEvent.Signal();
}
}).Start();
}
if (!countdownEvent.Wait(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException();
}
Assert.IsTrue(exceptions.Any());
Assert.AreEqual(typeof(AssertFailedException), exceptions.First().InnerException.GetType());
}
}
The WindowsIdentity (which is changed through impersonation) is stored in a SecurityContext. You can determine how this impersonation "flows" in various ways. Since you mentioned, you are using p/invoke, note the caveat in the SecurityContext documentation:
The common language runtime (CLR) is aware of impersonation operations
performed using only managed code, not of impersonation performed
outside of managed code, such as through platform invoke...
However, I'm not entirely certain that the impersonation is really inherited from one task to the other here, or whether the behavior you are observing is due to task inlining. Under certain circumstances, a new task might execute synchronously on the same thread-pool thread. You can find an excellent discussion of this here.
Nonetheless, if you want to make sure that certain tasks are always running under impersonation, while others don't, may I suggest looking into custom Task Schedulers. There is documentation on MSDN on how to write your own, including code-samples for a few common types of schedulers that you can use as a starting point.
Since impersonation is a per-thread setting, you could have your own task scheduler that keeps around one thread (or a few threads) that are running under impersonation when they execute tasks. This could also reduce the number of times you have to switch in and out of impersonation when you have many small units of work.
I am using MonoDevelop (.net 2.0) to develop a iOS and Android app. I use BeginGetResponse and EndGetResponse to asynchronously do a webrequest in a background thread.
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(onLogin), state);
However, the callback onLogin does seem to still be running on a background thread, no allowing me to interact with the UI. How do I solve this?
Can see that there are Android and iOS specific solutions but want a cross-platform solution.
Edit: From mhutch answer I've got this far:
IAsyncResult result = request.BeginGetResponse(o => {
state.context.Post(() => { onLogin(o); });
}, state);
Where state contains a context variable of type SynchronizationContext set to SynchronizationContext.Current
It complains that Post requires two arguments, the second one being Object state. Inserting state gives the error
Argument `#1' cannot convert `anonymous method' expression to type `System.Threading.SendOrPostCallback' (CS1503) (Core.Droid)
Both Xamarin.iOS and Xamarin.Android set a SynchronizationContext for the GUI thread.
This means you get the SynchronizationContext.Current from the GUI thread and pass it to your callback (e.g via the state object or captured in a lambda). Then you can use the context's Post method to invoke things on the main thread.
For example:
//don't inline this into the callback, we need to get it from the GUI thread
var ctx = SynchronizationContext.Current;
IAsyncResult result = request.BeginGetResponse(o => {
// calculate stuff on the background thread
var loginInfo = GetLoginInfo (o);
// send it to the GUI thread
ctx.Post (_ => { ShowInGui (loginInfo); }, null);
}, state);
I'm not sure if this works on Mono, but I usually do this on WinForm applications. Let's suppose you want to execute the method X(). Then:
public void ResponseFinished() {
InvokeSafe(() => X()); //Instead of just X();
}
public void InvokeSafe(MethodInvoker m) {
if (InvokeRequired) {
BeginInvoke(m);
} else {
m.Invoke();
}
}
Of course, this is inside a Form class.
I have a multi-thread windows service in .Net 3.5, and I am having some trouble to stop the service properly when more than one thread is created.
This service used to create only one thread to do all the work, and I just changed it to be multi-threaded. It works perfectly, but when the service is stopped, if more than one thread is being executed, it will hang the service until all the threads are completed.
When the service is started, I create a background thread to handle the main process:
protected override void OnStart(string[] args)
{
try
{
//Global variable that is checked by threads to learn if service was stopped
DeliveryConstant.StopService = false;
bool SetMaxThreadsResult = ThreadPool.SetMaxThreads(10, 10);
ThreadStart st = new ThreadStart(StartThreadPool);
workerThread = new Thread(st);
workerThread.IsBackground = true;
serviceStarted = true;
workerThread.Start();
}
catch (Exception ex)
{
//Log something;
}
Here is the StartThreadPool method:
//Tried with and without this attribute with no success...
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
public void StartThreadPool()
{
while (serviceStarted)
{
ProcessInfo input = new ProcessInfo();
try
{
int? NumPendingRequests = GetItems(50, (Guid?)input.ProcessID);
if (NumPendingRequests > 0)
{
input.ProcessType = 1;
input.ProcessID = Guid.NewGuid();
ThreadPool.QueueUserWorkItem(new WaitCallback(new DispatchManager().ProcessRequestList), input);
}
}
catch (Exception ex)
{
//Some Logging here
}
}
DeliveryConstant.StopService = true;
}
I created a static variable in a separated class to notify the threads that the service was stopped. When the value for this variable is true, all threads should stop the main loop (a for each loop):
public static bool StopService;
Finally, the OnStop method:
protected override void OnStop()
{
DeliveryConstant.StopService = true;
//flag to tell the worker process to stop
serviceStarted = false;
workerThread.Join(TimeSpan.FromSeconds(30));
}
In the ProcessRequestList method, at the end of every foreach, I check for the value of the StopService variable. If true, I break the loop.
Here is the problem:
The threads are created in chunks of 50 items. When I have 50 items or less in the database, only one thread is created, and everything works beautifully.
When I have more than 50 items, multiple threads will be created, and when I try to stop the service, it doesn't stop until all the background threads are completed.
From the logs, I can see that the method OnStop is only executed AFTER all threads are completed.
Any clue what could be changed to fix that?
This blog answer states that OnStop isn't called until all ThreadPool tasks complete, which is news to me but would explain your issue.
I've fielded many multi-threaded Windows Services but I prefer to create my own background threads rather than use the ThreadPool since these are long-running threads. I instantiate worker classes and launch their DoWork() method on the thread. I also prefer to use callbacks to the launching class to check for a stop signal and pass status rather than just test against a global variable.
You are missing memory barriers around accesses to StopService, which may be a problem if you have multiple CPUs. Better lock any reference object for ALL accesses to the shared variable. For example:
object #lock;
...
lock (#lock)
{
StopService = true;
}
Edit: As another answer has revealed, this issue was not a locking problem, but I am leaving this answer here as a thing to check with multithread synchronization schemes.
Making the shared variable volatile would work in many cases as well, but it is more complex to prove correct because it does not emit full fences.