0
\$\begingroup\$

I just wrote this now and didn't test it much. This isn't a replacement for std::mutex, but for something like fun. I may develop it to make it shared across processes by storing the counters in a shared memory. I don't know whether it's more efficient than the standard mutex or not. Also, the APC callbacks could be used to implement condition variable or promise and future.

void WINAPI wake_exclusive_locked_thread(ULONG_PTR inst) 
{
if (inst)
    CloseHandle((void*)inst);
}

class Mutex {
volatile long long exclusive_locked;
volatile unsigned long waiter_id;
volatile long long shared_lock;
public :
Mutex(const Mutex&) = delete;
Mutex(Mutex&&) = delete;
Mutex& operator=(const Mutex&) = delete;
Mutex& operator=(Mutex&&) = delete;

Mutex() : exclusive_locked(-1), waiter_id(0), shared_lock(0)
{
}

void lock()
{
    if (InterlockedIncrement64(&exclusive_locked) || InterlockedAdd64(&shared_lock,0))
    {
        unsigned long this_tid = cpp::this_thread::get_id();
        unsigned long old_waiter_id = InterlockedExchange(&waiter_id, this_tid);  // put this thread at the front
        InterlockedDecrement64(&exclusive_locked);

        while (InterlockedIncrement64(&exclusive_locked) || InterlockedAdd64(&shared_lock, 0)) // if the lock is free the inc will return 0
        {
            InterlockedDecrement64(&exclusive_locked);
            SleepEx(INFINITE, TRUE); // wait for the lock owner to unlock
        }

        InterlockedExchange(&waiter_id, old_waiter_id); // reback the old thread , may be null
    }

    // the thread now owns the lock
}

void unlock()
{
    unsigned long current_waiter_id = InterlockedAdd((volatile LONG*)&waiter_id, 0);
    InterlockedDecrement64(&exclusive_locked);
    void *handle = OpenThread(THREAD_ALL_ACCESS, FALSE, current_waiter_id);
    QueueUserAPC(wake_exclusive_locked_thread, handle, (ULONG_PTR)handle);
}

void lock_shared()
{

    if (InterlockedAdd64(&exclusive_locked,0) > -1)
    {
        unsigned long this_tid = cpp::this_thread::get_id();
        unsigned long old_waiter_id = InterlockedExchange(&waiter_id, this_tid); // put this thread at the front
        while (InterlockedAdd64(&exclusive_locked, 0) > -1)
        {
            SleepEx(INFINITE, TRUE); // wait for the lock owner to unlock
            void *handle = OpenThread(THREAD_ALL_ACCESS, FALSE, old_waiter_id); // notify the rest of the thrreads , if there is a write locker read lockers won't pass
            QueueUserAPC(wake_exclusive_locked_thread, handle, (ULONG_PTR)handle);
        }
    }
    InterlockedIncrement64(&shared_lock);
}

void unlock_shared()
{
    unsigned long current_waiter_id = InterlockedAdd((volatile LONG*)&waiter_id, 0);
    InterlockedDecrement64(&shared_lock);
    void *handle = OpenThread(THREAD_ALL_ACCESS, FALSE, current_waiter_id);
    QueueUserAPC(wake_exclusive_locked_thread, handle, (ULONG_PTR)handle);
}

};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.