I think the interview is asking you a trick question. If you could use static analysis to prevent deadlock... nobody would have deadlock!
Personally, when I look for deadlock I start by finding functions where the critical section spans more than the function call. For example
void func(){
lock(_lock){
func2();
}
}
It's not really clear what func2
is doing. Maybe it dispatches an event on the same thread, which would mean that event is still part of the critical section. Maybe it then locks on a different lock. Maybe it dispatches into the threadpool and no longer is re-entrant because its now on a different thread! These kinds of places are where you can start to see deadlock scenarios happening: when you have multiple non-reentrant lock locations.
Other times, when tracing deadlock scenarios, I backtrack and I try and find where are all the threads created. I give thought to each function and where it can actually be running. If you aren't sure, adding in logging to log where the function call came from can also help.
You can also avoid deadlocks by using lock free data structures (but those require just as much though to use). You want to minimize your access to the lock free structure because each time you access it it can change.
As mentioned in another answer, you can use mutexes with timeouts, but that isn't guaranteed to always work (what if your code needs to work longer than the timeout?). It was mentioned in another comment that this is maybe what the interviewer was asking for. I find that in production this isn't really a good idea. Timeouts vary all the time, maybe something took longer than expected to run and hit a timeout. I think its better to let it deadlock, take a process dump, then find exactly what was holding the locks and fix the problem. Of course, if your business requirements can't allow that, then you can use this as part of a defensive coding strategy along with smart lock placement choices.
I don't agree with your interview that locks always add a huge performance problem. Uncontended locks/mutexes/etc first test as a spinlock before handing off to the OS and spinlocks are cheap.
In general, the best way to avoid deadlock is to understand your program flow. Each time you introduce a new lock object think about where it is used and what uses it down the chain.