pseudonym117
pseudonym117

Reputation: 839

Waiting until another process locks and then unlocks a Win32 mutex

I am trying to tell when a producer process accesses a shared windows mutex. After this happens, I need to lock that same mutex and process the associated data. Is there a build in way in Windows to do this, short of a ridiculous loop?

I know the result of this is doable through creating a custom Windows event in the producer process, but I want to avoid changing this programs code as much as possible.

What I believe will work (in a ridiculously inefficient way) would be this (NOTE: this is not my real code, I know there are like 10 different things very wrong with this; I want to avoid doing anything like this):

#include <Windows.h>

int main() {
    HANDLE h = CreateMutex(NULL, 0, "name");
    if(!h) return -1;

    int locked = 0;
    while(true) {
        if(locked) {
            //can assume it wont be locked longer than a second, but even if it does should work fine
            if(WaitForSingleObject(h, 1000) == WAIT_OBJECT_0) {
                // do processing...
                locked = 0;
                ReleaseMutex(h);
            }
        // oh god this is ugly, and wastes so much CPU...
        } else if(!(locked = WaitForSingleObject(h, 0) == WAIT_TIMEOUT)) {
            ReleaseMutex(h);
        }
    }
    return 0;
}

If there is an easier way with C++ for whatever reason, my code is actually that. This example was just easier to construct in C.

Upvotes: 0

Views: 1849

Answers (2)

MSalters
MSalters

Reputation: 179819

Tricky. I'm going to answer the underlying question: when is the memory written?

This can be observed via a four step solution:

  1. Inject a DLL in the watched process
  2. Add a vectored exception handler for STATUS_GUARD_PAGE_VIOLATION
  3. Set the guard page bit on the 2 MB memory range (finding it could be a challenge)
  4. From the vectored exception handler, inform your process and re-establish the guard bit (it's one-shot)

You may need only a single guard page if the image is always fully rewritten.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 596176

You will not be able to avoid changing the producer if efficient sharing is needed. Your design is fundamentally flawed for that.

A producer needs to be able to signal a consumer when data is ready to be consumed, and to make sure it does not alter the data while it is busy being consumed. You cannot do that with a single mutex alone.

The best way is to have the producer set an event when data is ready, and have the consumer reset the event when the data has been consumed. Use the mutex only to sync access to the data, not to signal the data's readiness.

#include <Windows.h>

int main()
{
    HANDLE readyEvent = CreateEvent(NULL, TRUE, FALSE, "ready");
    if (!readyEvent) return -1;

    HANDLE mutex = CreateMutex(NULL, FALSE, "name");
    if (!mutex) return -1;

    while(true)
    {
        if (WaitForSingleObject(readyEvent, 1000) == WAIT_OBJECT_0)
        {
            if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
            {
                // process as needed...
                ResetEvent(readyEvent);
                ReleaseMutex(mutex);
            }
        }
    }

    return 0;
}

If you can't change the producer to use an event, then at least add a flag to the data itself. The producer can lock the mutex, update the data and flag, and unlock the mutex. Consumers will then have to periodically lock the mutex, check the flag and read the new data if the flag is set, reset the flag, and unlock the mutex.

#include <Windows.h>

int main()
{
    HANDLE mutex = CreateMutex(NULL, FALSE, "name");
    if (!mutex) return -1;

    while(true)
    {
        if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
        {
            if (ready)
            {
                // process as needed...
                ready = false;
            }
            ReleaseMutex(mutex);
        }
    }

    return 0;
}

So either way, your logic will have to be tweaked in both the producer and consumer.

Otherwise, if you can't change the producer at all, then you have no choice but to change the consumer alone to simply check the data for changes peridiodically:

#include <Windows.h>

int main()
{
    HANDLE mutex = CreateMutex(NULL, 0, "name");
    if (!mutex) return -1;

    while(true)
    {
        if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
        {
            // check data for changes
            // process new data as needed
            // cache results for next time...
            ReleaseMutex(mutex);
        }
    }

    return 0;
}

Upvotes: 2

Related Questions