Also, note that the examples on this page depend on atomic operations in addition to semaphores, not semaphores alone. If you only use semaphores, you must also use them to protect the accesses that are required to be atomic, which complicates the code.
Let's look at LightWeightMutex. This object is so light weight that we can't even ask it whether it is currently locked, and who the owner is. These features are important for error detection and debugging: real-world requirements that actual mutex implementations satisfy.
Another comment; I find the following completely pointless:
void lock()
{
if (m_contention.fetch_add(1, std::memory_order_acquire) > 0)
{
m_semaphore.wait();
}
}
A semaphore is already supposed to implement counting. What we have here is an implementation of a counting semaphore, using a semaphore.That is to say, to implement a light weight lock using a semaphore, we actually need only this:
void lock() { m_semaphore.wait(); }
The semaphore already has a built in atomic variable equivalent to the m_contention. The atomic increment and test wrapped around this is redundant, and has nothing to do with implementing a lock with a semaphore.