Interrupt context
When a device raises an interrupt, the CPU stops the running code at the next instruction boundary and jumps to a handler. The handler is not a thread: it borrows the stack of whatever it interrupted and runs with interrupts masked, so nothing else runs on that CPU until it returns, not even the timer tick that drives preemption. A handler runs in interrupt context, and everything else in thread context.
A handler therefore cannot wait. Waiting means giving the CPU to another thread, and a handler has no thread to park: one that sleeps, waits for an event or spins on a lock the interrupted code holds waits forever. Kernels split interrupt work in two. The top half, in the handler, acknowledges the device and wakes whoever waits. The bottom half runs later in thread context, where it may block and take ordinary locks.
Data a handler shares with a thread needs a spin lock that disables interrupts while held, or the handler can fire on the holder's CPU and spin forever on a thread that cannot run until the handler returns. Such a lock is held briefly, never across a wait.
In the driver kit, a handler acknowledges through its register window and hands off through its InterruptContext: Mask() masks its source, Signal(DeviceEvent) wakes a waiting thread, and Schedule(WorkItem) queues the bottom half on the kit worker, while DeviceLock.Acquire() is the spin lock that disables interrupts. The guard catches calls to DeviceBinding methods and to some access-object members, such as UsbAccess transfers and Ps2Access.TryCommand: it halts the kernel in a real interrupt, naming the member, and throws in a synthetic dispatch, where the kit records the fault and masks the source.
The kit also adds a rule of its own: a driver's handler must not allocate. The kernel's own handlers do, and the garbage collector tolerates it; this is a safety rule, because an allocation can pull in runtime machinery that misbehaves in interrupt context, notably interface dispatch resolution and exception throwing. Nothing detects an allocation: the guard runs only inside those kit members, never in the driver's own code. See The two execution contexts and the guard, Work items and Device locks.