2

In a DriverKit extension, I would like to block a call from a user client until a specific hardware interrupt fires. Since there are no semaphores available (Does the DriverKit SDK support semaphores?), I've reached for a very basic spinlock using an _Atomic(bool) member and busy waiting:

struct IVars
{
  volatile _Atomic(bool) InterruptOccurred = false;
}

// In the user client method handler
{
  // Clear the flag
  atomic_store(&ivars->InterruptOccurred, false);

  // Set up the interrupt on the device
  ...

  // Wait for the interrupt
  while (!atomic_load(&ivars->InterruptOccurred))
  {
    IOSleep(10);
  }
}

// In the interrupt handler
{
  bool expected = false;
  if (atomic_compare_exchange_strong(&ivars->InterruptOccurred, &expected, true))
  {
    return;
  }

  // Proceed with normal handling if the user client method is not waiting
}

The user client method is called infrequently and the interrupt is guaranteed to fire within 100ms, so in principle busy waiting should be acceptable, but I am not very happy with the solution. I haven't worked with spinlocks before and they make me feel rather uneasy.

I would like to avoid taking an IOLock in the interrupt handler. Is there any other synchronization primitive in DriverKit I could reach for? I guess a cleaner way to handle this would be for the user client method to accept a callback that fires on the interrupt, but that would still require synchronization with the interrupt handler and would complicate the client application code.

pmdj
  • 22,018
  • 3
  • 52
  • 103

1 Answers1

3

Preliminaries

I would like to avoid taking an IOLock in the interrupt handler.

I assume you're aware that, this being DriverKit, this isn't running in the context of a primary interrupt controller, but you're already behind a layer of Mach messaging, kernel/user context switch, and IODispatchQueue serialisation?

Possible solutions:

Since there are no semaphores available[…]

OSAction

The OSAction class contains a set of methods for sleeping in a thread until the action is invoked. (WillWait/Wait/EndWait) This might be a feasible way of implementing what you're trying to do. As usual, the documentation is in the header/iig file but hasn't made it into the web-based API docs.

IODispatchQueue

As of DriverKit 21 (macOS 12), you also get Apple's simpler Sleep/Wakeup event system baked into IODispatchQueue, which you might be familiar with from the kernel. (It is also similar to pthreads condition variables.) Note you need to create the queue with the kIODispatchQueueReentrant option in this case.

From DriverKit 22 (macOS 13/iPadOS) on, there's also a version with a deadline for the sleep SleepWithDeadline.

Async callbacks

I guess a cleaner way to handle this would be for the user client method to accept a callback that fires on the interrupt, but that would still require synchronization with the interrupt handler and would complicate the client application code.

If you're happy calling the async callback in the app on every interrupt, there's not really any synchronisation needed, you can just invoke the same OSAction repeatedly. Even if you want to only invoke the async call on the "next" interrupt, atomic compare-and-swap should be sufficient for the interrupt handler to claim the OSAction* pointer.

Important note:

With all of these potential solutions except IODispatchQueue::Sleep and the async callback: bear in mind that sleeping in the context of a user client external method will block the dispatch queue and thus any other calls to external methods in that user client will fail to make progress. (As well as any other methods scheduled to that queue.)

pmdj
  • 22,018
  • 3
  • 52
  • 103
  • 1
    Great answer, thanks a lot! I was hoping you'd chime in. :-) – Jonas Due Vesterheden Feb 15 '23 at 11:38
  • Hi @pmdj, I have been searching for relevant example code for a while now, but unfortunately, I haven't been able to find any resources on how to use it. If you happen to have some free time, would you consider sharing it on your GitHub repository? – xmx Aug 07 '23 at 13:26