Disabling Interrupts on the Traditional Mac OS
By Quinn "The Eskimo!"
This Technote describes how to disable interrupts on the traditional Mac OS. It also includes a long discussion of why you should not disable interrupts, and outlines other system services that you can use to avoid disabling interrupts.
This Note is directed at developers who are building kernel-level software, such as device drivers, or application software that makes heavy use of Mac OS "interrupt time." In general, application developers should not need to disable interrupts.
DTS recommends that third-party developers avoid disabling interrupts on Mac OS, although we recognize that there are circumstances for which disabling interrupts is the only solution. The purpose of this Note is to highlight the possible alternatives to disabling interrupts and -- if you decide that none of these meet your needs -- to minimize errors when performing this tricky task. This note should not be construed as an encouragement to disable interrupts needlessly.
The note is broken up into four sections:
Why is DTS so against disabling interrupts? It is because:
This section will explore each area in turn.
Disabling interrupts increases the interrupt latency of the system. This is bad for system functions, like Sound Manager, that require good interrupt latency in order to operate correctly. While somewhat long in the tooth, DTS Technote HW 16 "I Was a Teenage DMA Junkie" describes some background on this issue.
As described in the following section, there is no way to disable interrupts quickly on Mac OS. If you're running PowerPC code, you have to take a Mixed Mode Manager switch in order to disable interrupts. If you're building 680x0 software, it's possible that your code will be run on a real 680x0 microprocessor with virtual memory enabled, in which case the modification of the SR register is a privileged operation which the system must emulate for you. Both of these represent a performance penalty.
The Mac OS provides many low-level primitives that you can use to avoid disabling interrupts. While it's true that many of these routines eventually do disable interrupts, using them saves you some effort and allows Apple to improve the system "behind your back."
This section describes the traditional Mac OS interrupt architecture, which is modeled directly after the interrupt architecture on the 680x0 microprocessors. If you're already familiar with the 680x0 interrupt architecture, you may want to skip this section.
About 680x0 Interrupt Levels
The 680x0 SR register contains a three bit field that determines the current interrupt mask, a value from 0 to 7. If the priority of an incoming interrupt (the interrupt's level) is greater than the current interrupt mask, the 680x0 (or the emulated 680x0, if you're running on a PowerPC-based computer) will raise the interrupt mask to that level and service the interrupt.
The following table summarizes the common uses for the various interrupt levels:
The 680x0 SR register is a privileged register; it can only be accessed from code running in 680x0 supervisor mode. When you access the SR register from user mode, the 680x0 takes a privilege violation exception. The traditional Mac OS catches this exception and emulates the offending instruction. So, apart from the slow down caused by the exception, you can ignore this restriction and access the SR register from any 680x0 software.
For more information on SR register emulation, consult Technote 1094 "Virtual Memory Application Compatibility".
Interrupt Levels on a PowerPC
The raw PowerPC processor only has a single-interrupt-state bit: interrupts are either masked or they aren't. The interrupt mask bit is a privileged bit, so you must be running the native PowerPC processor in native supervisor mode to be able to access it. However, Mac OS runs all PowerPC code (except the nanokernel, see the next section) in user mode, so you cannot access the PowerPC interrupt mask bit from PowerPC code.
Mac OS Interrupt Architecture on PowerPC
As described above, the 680x0 and PowerPC microprocessors have quite different interrupt architectures. One of the key features of the Mac OS interrupt architecture on PowerPC-based computers is that it emulates the 680x0 interrupt architecture. This is necessary because a significant quantity of code (both in the traditional Mac OS and third party) needs to disable interrupts, including selectively masking interrupts at a specific level.
For example, consider the traditional Mac OS serial driver. As serial interrupts occur at level 2, the serial driver interrupt handler knows that, as long as it keeps the interrupt mask at 2 or higher, no other serial interrupt can occur. Many drivers use this assumption to provide concurrency control for their global data structures.
So, for compatibility purposes, all interrupts on Power Macintosh computers are prioritorized as if they were on a 680x0-based computer. When external hardware interrupts the PowerPC processor, the interrupt is initially serviced by the nanokernel, which takes one of two actions depending on the state of the machine:
The emulator executes 680x0 instructions atomically with respect to interrupts, as they were in the original 680x0 processors. This preserves the atomicity implied in 680x0 interrupt-handling code. The dynamic recompiling emulator may check for interrupts with less granularity due to the larger sections of native code it builds.
In summary, on a Power Macintosh, all interrupts are routed by the nanokernel through the emulator for servicing to achieve faithful emulation of 680x0 interrupts levels and to keep 680x0 instructions indivisible.
Theoretical Background for
oldMask = SetInterruptMask(7); // Interrupts are now disabled, do your stuff! (void) SetInterruptMask(oldMask);
The library contains but two entry points:
extern pascal UInt16 GetInterruptMask(void); extern pascal UInt16 SetInterruptMask(UInt16 newMask);
GetInterruptMask function returns the current 680x0 interrupt mask as a value from 0 to 7. The
SetInterruptMask function sets the current 680x0 interrupt mask as a value from 0 to 7. It also returns the prior interrupt mask.
This section contains some important rules. Rules must sometimes be broken, but you should think very carefully before breaking these!
GetInterruptMaskfunction to determine whether your code is running at "interrupt time." There are numerous ways a Mac OS computer can have interrupts enabled (an interrupt mask of 0) but still be running at "interrupt time." These include VBLs, Deferred Tasks, and PCI Secondary Interrupts.
Before disabling interrupts, you should investigate the following system services to see if you can use them instead.
The ancestors of all atomic operations on the Mac OS are the OS Utilities routines
If you place all your critical sections in deferred tasks, you can take advantage of the Deferred Task Manager's guarantee that all deferred tasks are serialized.
Open Transport Utilities
Open Transport provides a plethora of kernel-level services, including the following interrupt safe constructs:
All of these OT primitives are implemented completely in native code on the PowerPC.
DTS recommends that developers avoid disabling interrupts. Mac OS provides many alternatives to disabling interrupts. If none of these alternatives meet your needs, you can disable interrupts by setting the interrupt mask in the 680x0 SR register. To do this from PowerPC code, you must call 680x0 software using Mixed Mode Manager. If you do this, you should use the code from
Acrobat version of this Note (34K).
Thanks to Mark Cookson, Pete Gontier, Jim Luther, Jim Murphy, Paul Resch, and Alex Rosenberg.
To contact us, please use the Contact Us page.