ADC Home > Reference Library > Technical Notes > Legacy Documents > Mac OS 9 & Earlier >
Legacy Document
Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.
Current information on this Reference Library topic can be found here:
|
IntroductionDTS 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:
RationaleWhy is DTS so against disabling interrupts? It is because:
This section will explore each area in turn. LatencyDisabling 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. Performance PenaltyAs 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. AvoidabilityThe 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." Background MaterialThis 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 LevelsThe 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 PowerPCThe 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 PowerPCAs 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
|
Note: |
Using InterruptDisableLib
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:
- Add the "InterruptDisableLib.c" file to your program. If you're using an integrated development environment that supports C code, you will be able to add it directly to your project. Otherwise you may need to compile it separately and add it as an object file.
- Include the appropriate interface file. For C/C++ programmers, you should include "InterruptDisableLib.h". Pascal programmers should use "InterruptDisableLib.p".
- When you need to disable interrupts, do so using:
oldMask = SetInterruptMask(7); // Interrupts are now disabled, do your stuff! (void) SetInterruptMask(oldMask); |
Library Reference
The library contains but two entry points:
extern pascal UInt16 GetInterruptMask(void); extern pascal UInt16 SetInterruptMask(UInt16 newMask); |
The 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.
Gotchas
This section contains some important rules. Rules must sometimes be broken, but you should think very carefully before breaking these!
- Never never never use the
GetInterruptMask
function 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. - Never lower the interrupt mask inside an interrupt handler. For example, if your interrupt handler is entered with the interrupt mask set to 2, never lower the mask below that. The reason? A hardware driver may have set the interrupt mask to 1 to prevent it being reentered. If your interrupt happens at level 2, and then you lower the interrupt mask to 0, you will allow interrupts that the other driver is not expecting.
- Always restore the old interrupt mask. Never assume that you're running with a particular interrupt mask, and restore that mask by explicitly setting it. Always save the old mask and restore it when you're done.
Avoidance
Before disabling interrupts, you should investigate the following system services to see if you can use them instead.
OS Utilities
The ancestors of all atomic operations on the Mac OS are
the
OS
Utilities routines Enqueue
and
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.
Deferred Tasks
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:
- LIFO queues
- atomic bit and arithmetic operations
- atomic compare and swap
- Open Transport deferred tasks
OTGate
(provides critical section support)- memory allocation
All of these OT primitives are implemented completely in native code on the PowerPC.
DriverServicesLib
If you're writing a PCI device driver,
DriverServicesLib
provides myriad low-level queue manipulation and atomic
operations.
The 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.
680x0 Atomic Instructions
As described
above, 680x0 instructions remain atomic even
when emulated on the Power Macintosh. Thus, run-of-the-mill
680x0 instructions like
addq
and bset
are all atomic
operations on both 680x0- and
PowerPC-based computers.
PowerPC Atomic Instructions
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.
Important: |
Summary
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
InterruptDisableLib
to prevent common mistakes, and make sure you read the
list of caveats in this Technote.
References
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.
Inside Macintosh: PowerPC System Software, Chapter 2 Mixed Mode Manager
Inside Macintosh: Operating System Utilities, Chapter 6 Queue Utilities
Inside Macintosh: Processes, Chapter 6 Deferred Task Manager
See the Open Transport web page for references to Open Transport technical documentation.
Designing PCI Cards and Drivers for Power Macintosh Computers
Downloadables
Acrobat version of this Note (72K). |
||
Binhexed Routine InterruptDisableLib (179K). |