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 Teenaged 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.
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 prioritized 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
A code sample called
InterruptDisableLib is provided for
you as an attachment to this technote. The code uses the
technique discussed in the previous section to provide
easy-to-use control over the 680x0 interrupt mask from classic
68K, PowerPC, and CFM-68K code. The code is structured as a
complete library which you can drop in to your project, with
C and Pascal interfaces, and a C implementation.
The library was compiled and tested using the Metrowerks CodeWarrior Pro C and Pascal compilers; however, you should be able to use the source with any C, C++, or Pascal compiler. To use it in your project, you need to take the following steps:
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
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
Dequeue. These routines are available on all
Mac OS computers and provide simple atomic queue
manipulation. They are, however, implemented as 680x0 code, so using them will cause
a Mixed Mode Manager switch.
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 provides a plethora of kernel-level services, including the following interrupt safe constructs:
OTGate(provides critical section support)
All of these OT primitives are implemented completely in native code on the PowerPC.
If you're writing a PCI device driver,
provides myriad low-level queue manipulation and atomic
DriverServicesLib queue manipulation routines are
similar to the OS Utilities routines except that they have
fast-code paths that avoid
Mixed Mode Manager switches when adding to an empty queue or
removing from a one-element queue.
above, 680x0 instructions remain atomic even
when emulated on the Power Macintosh. Thus, run-of-the-mill
680x0 instructions like
bset are all atomic
operations on both 680x0- and
The PowerPC processor contains two special instructions,
Load Reserved (
lwarx) and Store Conditional
stwcx), which you can use to implement atomic
operations. Most compilers provide access to these
instructions via intrinsic functions.
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
to prevent common mistakes, and make sure you read the
list of caveats in this Technote.
Technote HW 16 "I Was a Teenaged DMA Junkie"
Technote 1094 "Virtual Memory Application Compatibility" discusses the SR instruction emulation done by the Virtual Memory Manager on 680x0-based computers.
PowerPC Microprocessor Family: The Programming Environments For 32-Bit Microprocessors discusses the native PowerPC interrupt model.
M68000 Family Programmer's Reference Manual discusses the 680x0 interrupt model.
See the Open Transport web page for references to Open Transport technical documentation.
Acrobat version of this Note (72K).
Binhexed Routine InterruptDisableLib (179K).
Contact ADC | ADC Site Map | ADC Advanced Search
|For information about Apple Products, please visit Apple.com.|
Contact Apple | Privacy Notice
Copyright © 2002 Apple Computer, Inc. All rights reserved.