As mentioned in another answer, you need Fair Mutex, and Ticket Lock may be one of ways to implement it.
There's another way, based on binary semaphore, and it is actually close to what Critical Section used to be. Like this:
class old_cs
{
public:
old_cs()
{
event = CreateEvent(NULL, /* bManualReset = */ FALSE, /* bInitialState =*/ TRUE, NULL);
if (event == NULL) throw std::runtime_error("out of resources");
}
~old_cs()
{
CloseHandle(event);
}
void lock()
{
if (count.fetch_add(1, std::memory_order_acquire) > 0)
WaitForSingleObject(event, INFINITE);
}
void unlock()
{
if (count.fetch_sub(1, std::memory_order_release) > 1)
SetEvent(event);
}
old_cs(const old_cs&) = delete;
old_cs(old_cs&&) = delete;
old_cs& operator=(const old_cs&) = delete;
old_cs& operator=(old_cs&&) = delete;
private:
HANDLE event;
std::atomic<std::size_t> count = 0;
};
You may find the following in Critical Section Objects documentation:
Starting with Windows Server 2003 with Service Pack 1 (SP1), threads
waiting on a critical section do not acquire the critical section on a
first-come, first-serve basis. This change increases performance
significantly for most code. However, some applications depend on
first-in, first-out (FIFO) ordering and may perform poorly or not at
all on current versions of Windows (for example, applications that
have been using critical sections as a rate-limiter). To ensure that
your code continues to work correctly, you may need to add an
additional level of synchronization. For example, suppose you have a
producer thread and a consumer thread that are using a critical
section object to synchronize their work. Create two event objects,
one for each thread to use to signal that it is ready for the other
thread to proceed. The consumer thread will wait for the producer to
signal its event before entering the critical section, and the
producer thread will wait for the consumer thread to signal its event
before entering the critical section. After each thread leaves the
critical section, it signals its event to release the other thread.
So the algorithm inthis post is a simplified version of what Critical Section used to be in Windows XP and earlier.
The above algorithm is not a complete critical section, it lack recursion support, spinning, low resources situation handling.
Also it relies on Windows Event fairness.