Solving Likely Problems in Your Multithreaded Code
msdn.microsoft.com
msdn.microsoft.com
"Smart Unit Tests" [1] is the most recent in a long list of things tucked away in Ultimate.
[1] https://msdn.microsoft.com/library/dn823749(v=vs.140).aspx
If making the world's code quality better is a priority for you, I think the solution is to switch to free languages and toolchains and help improve them. I'm not saying this from a "shut up or leave" kind of mentality: this is exactly why I moved out of the C#/.NET ecosystem.
class BankAccount {
internal static void Transfer(
BankAccount a, BankAccount b, decimal delta) {
Withdraw(a, delta);
Deposit(b, delta);
}
// As before
}
Withdraw() and Deposit() either do their own locking, and we get a race trying to use them together, or we push locking up the chain to their users. The former solution is unacceptable, and the latter just punts the problem--what if we want to call Transfer() and some other function together without a possible race condition between them?Assuming you've got some sort of database or something that has a notion of a transaction use a TransactionScope [1] with something like IsolationLevel.RepeatableRead. They are quite flexible. They are created with thread affinity [2] so any operation on the same thread can detect their presence and enlist in them. Make a TransactionScope, call your methods in whichever order you want, commit it.
[1] https://msdn.microsoft.com/en-us/library/system.transactions...
[2] Sort of. They can be shared between threads but the easiest way to use them is to treat them like they're bound to a thread.
Ensure that the only way to construct something that accesses a BankAccount is as an AccountAction. You're automatically assured that any code calls the "actor" eventually, because there's no way to e.g. print an AccountAction[String] to standard output - sooner or later you have to make the call. User code can still have races if it tries (e.g. if it deliberately calls the "actor" separately for the withdraw and the deposit), but such code is much more obviously wrong from reading and looking at the types (particularly if you call your actor call something like "unsafeTransaction"). If you've got a single consistent point of entry/exit (e.g. this is a message-processing system), you can put the actor call there.
The only hard part is making this easy enough to use that it doesn't overcomplicate the code. Provide easy syntax for composing two AccountActions - something that's as clear and simple as just calling two methods, but with just enough difference to make it clear from the code whether you're using one or not - e.g. non-action calls use "=", action calls use "<-". Make a library of functions for doing things like "perform a list of AccountActions as a single, atomic AccountAction". Better yet, make AccountAction conform to some interface such that that library already exists.
(Of course by now you've probably guessed that AccountAction is just an alias for the reader monad. But the naming is valuable; it keeps everything more concrete and clear)
http://research.microsoft.com/pubs/74063/beautiful.pdf
or we push locking up the chain to their users
That's still prone to deadlock. Suppose each account has its own lock. Then to transfer from account A to B, the code might look like
A.lock(); B.lock();
A.withdraw( amount );
B.deposit( amount );
B.unlock(); A.unlock();
If another transfer occurs in the reverse direction at exactly the same time, the code deadlocks in the middle of the first line.SPJ explains this and other cases pretty well.