I am writing a class that I know that needs a lock, because the class must be thread safe. But as I am Test-Driven-Developing I know that I can’t write a line of code before creating a test for it. And I find very difficult to do since the tests will get very complex in the end. What do you usually do in those cases? There is any tool to help with that?
This question is .NET specific
Someone asked for the code:
public class StackQueue
{
private Stack<WebRequestInfo> stack = new Stack<WebRequestInfo>();
private Queue<WebRequestInfo> queue = new Queue<WebRequestInfo>();
public int Count
{
get
{
return this.queue.Count + this.stack.Count;
}
}
public void Enqueue(WebRequestInfo requestInfo)
{
this.queue.Enqueue(requestInfo);
}
public void Push(WebRequestInfo requestInfo)
{
this.stack.Push(requestInfo);
}
private WebRequestInfo Next()
{
if (stack.Count > 0)
{
return stack.Pop();
}
else if (queue.Count > 0)
{
return queue.Dequeue();
}
return null;
}
}
When writing multi-threaded code, you must use your brain even more than the usual. You must reason logically about every single line of code, whether it is thread safe or not. It’s like proving the correctness of a mathematical formula – you can not prove things like “N + 1 > N for all N” by just giving examples of values of N with which the formula is true. Similarly, proving that a class is thread-safe is not possible by writing test cases that try to expose problems with it. With a test it’s only possible to prove that there is a fault, but not that there are no faults.
The best thing that you can do, is to minimize the need for multi-threaded code. Preferably the application should have no multi-threaded code (for example by relying on thread-safe libraries and suitable design patterns), or it should be restricted to a very small area. Your
StackQueueclass looks like simple enough, so that you can make it safely thread-safe with a little thinking.Assuming that the
StackandQueueimplementations are thread-safe (I don’t know .NET’s libraries), you just need to makeNext()thread-safe.Countis already thread-safe as it is, because no client can use the value returned from it safely without using client-based locking – state dependencies between methods would otherwise break the code.Next()is not thread-safe, because it has state dependencies between methods. If threads T1 and T2 callstack.Countat the same time and it returns 1, then one of them will get the value withstack.Pop(), but the other will callstack.Pop()when the stack is empty (which then appears to throwInvalidOperationException). You will need a stack and queue with non-blocking versions ofPop()andDequeue()(ones that return null when empty). Then the code would be thread-safe when written like this: