Live by these rules and no one gets hurt:
1. You need locks, even if you do not have SMP.
2. Lock data, not code. Your locks should be associated with specific data, not regions of code.
3. There is a fine line between too coarse locking and too fine locking. On one hand, fine-grained locking results in better scalability via reduced lock contention. On the other hand, the more locks you implement the more memory and processing cycles you use and locking semantics grow more complicated.
4. Consider not using spinlocks when the lock-held time is long. The notion of “long” varies from person to person, but somewhere around a maximum of 1-5ms on a modern system is a good rule of thumb. Remember that threads waiting on a spinlock busy loop, accomplishing nothing. Long lock-held times, subsequently, result in high-lock contention under SMP and poor latency under the preemptible kernel. Conversely, because semaphores cause contending threads to sleep and execute other code, they are a smarter solution when the wait time can be high.
5. Design your locking semantics wisely and stick to them.