macoskernellockingkernel-extensionmach

IOLockWakeup and IOLockSleep


I am curious about the event parameter that gets passed to IOLockWakeup and IOLockSleep{Deadline}.

i understand that the event is an address that gets passed to both functions. i am assuming this address is used to essentially notify the thread.

so my question is: assuming i is an int, and we are using its address, how do these functions know when to sleep and wakeup?

is the assumption that:

and when we keep calling these functions (in a workloop context) are the contents of the event parameter automatically set to zero when iolocksleep* is called (and when it wakes up), since iolockwakeup presumably changs this to a nonzero value?


Solution

  • You'll notice that the event parameter is of type void*, not int*:

    int IOLockSleep( IOLock * lock, void *event, UInt32 interType);
    

    The event parameter is an arbitrary pointer, it’s never dereferenced, and it doesn’t matter what’s stored there, it's used purely for identification purposes: so for example don't pass NULL, because that's not a unique value.

    IOLockSleep always suspends the running thread, and IOLockWakeup wakes up any thread that’s sleeping on that address. If no such thread is waiting, nothing at all happens. This is why you’ll usually want to pair the sleep/wakeup with some condition that’s protected by the lock, and send the wakeup while holding the lock - the thing to avoid is going to sleep after the wakeup was sent, in which case your sleeping thread might sleep forever.

    So, you'll have some condition for deciding whether or not to sleep, and you'll update that condition before calling wakeup, while holding the lock:

    IOLock* myLock;
    bool shouldSleep;
    
    …
    
        // sleep code:
        IOLockLock(myLock);
        while (shouldSleep)
        {
            IOLockSleep(myLock, &shouldSleep, THREAD_UNINT);
        }
        IOLockUnlock(myLock);
    
    …
    
        // wakeup code:
        IOLockLock(myLock);
        shouldSleep = false;
        IOLockWakeup(myLock, &shouldSleep, true /* or false, if we want to wake up multiple sleeping threads */);
        IOLockUnlock(myLock);
    

    Here, I've used the address of shouldSleep for the event parameter, but this could be anything, it's just convenient to use this because I know no other kext will be using that pointer, as no other kext has access to that variable.