|
Technote 1104Interrupt-Safe RoutinesBy Brian Bechtel and Quinn "The Eskimo" |
CONTENTSIntroduction
| The traditional Mac OS has a badly defined set of heterogeneous programming environments. In some of these environments, your code can access some system services but not others. Furthermore, the names given to these environments are often overloaded and confusing. This results in a lot of programmer confusion. This Technote attempts to clear up this confusion by assigning each of the execution levels a unique name, describing how and why your code might find itself running at a particular execution level, and outlining the restrictions your code might face when running at that level. This Technote is important for anyone programming any Mac OS code that might run at "interrupt time," and vital for anyone doing system-level programming under the traditional Mac OS. |
IntroductionThere has been much confusion about which Mac OS routines can be used at interrupt time and which cannot. This Technote lists the Mac OS routines which can be used at interrupt time. This Technote list routines which are safe at interrupt time, rather than those that are unsafe. As the system evolves, more routines are added, and it may become necessary to do more work in existing routines. So routines that just happen to be interrupt-safe may become otherwise. Thus, any list of interrupt-unsafe routines will grow over time, and consequently is hard to maintain. A list of routines that are safe is more likely to remain accurate. DTS recommends that you assume all routines absent from this list are unsafe to call at interrupt time. This is a general defensive programming guideline, not a definitive pronouncement. If you know of a routine which you always considered to be interrupt-safe that is not listed here, please let us know. As an example of how your feedback is valuable to us, the first version of this technote failed to mention that A interrupt-safe routine can become unsafe if it is patched inappropriately. When you patch a routine which is interrupt-safe, you should assume that your patch is running at interrupt time and avoid doing things that are illegal at interrupt time.
The old Inside Macintosh, volume 6, appendix B had a list of routines which can be called at interrupt time. This Technote is an updated list of those routines, along with comments as appropriate. Do not rely on the list of interrupt-safe routines in Inside Macintosh, volume 6, appendix B. |
Execution LevelsThe traditional Mac OS supports the following execution levels:
In addition, the native device driver model defines the following execution levels:
Since these execution levels are modeled after the execution levels supported by Copland, their implementation on the traditional Mac OS is somewhat imprecise. In broad terms, the following analogies apply:
However, the distinction between these analogous pairs is important in certain circumstances, as explained later in this note.
This remainder of this section describes each of the execution levels in detail. Hardware InterruptWhat is it?Hardware interrupt-level execution happens as a direct result of a hardware interrupt request. Software executed at hardware interrupt level includes installable interrupt handlers for NuBus and other devices, as well as interrupt handlers supplied by Apple. How do you get there?You get to hardware interrupt level as the direct result of a installing a hardware interrupt handler (e.g., a NuBus handler installed with What can you do there?Hardware interrupts are considered "interrupt time" as defined by the toolbox, Virtual Memory Manager, and Open Transport. The associated restrictions are described later in this document. In addition, you should make every attempt to minimize the amount of time you spend at hardware interrupt level. Hardware interrupt level requires that all interrupts with lower interrupt priority be disabled for the duration of the hardware interrupt handler. The longer you spend in your hardware interrupt handler, the longer the interrupt latency of the computer will be. Increased interrupt latency may result in a poor user experience -- such as sound breakup or mouse tracking problems -- or worse. If you need to do extended processing at interrupt time, you should schedule a deferred task (using Is paging safe?Paging is not safe at hardware interrupt level unless the interrupt has been deferred using
Deferred TaskWhat is it?A deferred task is a mechanism whereby hardware interrupt-level code can schedule a routine to be executed when interrupts have been re-enabled, but before the return from the interrupt. Hardware interrupt handlers do this in order to minimize the amount of time spent in the hardware interrupt handler, and thereby minimize system interrupt latency. How do you get there?The most common way to get to deferred task level is to have your hardware interrupt handler call You can also get to deferred task level by being called by something that is executing at deferred task level. A good example of this are Open Transport notifier functions, which are often called at deferred task level. What can you do there?Deferred tasks are considered "interrupt time" as defined by the toolbox. The associated restrictions are described later in this document. Is paging safe?Paging is safe at deferred task level. Special ConsiderationsAnother useful feature of deferred tasks is that they are serialized. The system will not interrupt a deferred task in order to run another deferred task. This makes a really neat mutual exclusion mechanism. System TaskWhat is it?System task level is the level at which most application code runs. The name is derived from an obsolete Mac OS system call,
How do you get there?An application's main entry point is called at system task level. Cooperatively scheduled Thread Manager threads also run at system task level. For other types of code, Technote 1033: "Interrupts in Need of (a Good) Time" describes how to get to system task level from interrupt level. What can you do there?Code running at system task level is not considered "interrupt time" by anything. You can do virtually anything at system task level. Is paging safe?By default paging is safe at system task level. The exceptions occur when your code is accessing some resource that the system needs to support paging. For example, if you obtain exclusive access to the SCSI bus using Native Hardware InterruptWhat is it?Native hardware interrupt level is virtually identical to normal hardware interrupt level except that it only comes into play on machines that have the native driver architecture.
How do you get there?You get to native hardware interrupt level by installing a hardware interrupt handler using the native Interrupt Manager, or by being called by something that is directly invoked by such a handler. What can you do there?Native hardware interrupts are considered "interrupt time" as defined by the toolbox, Virtual Memory Manager and Open Transport. The associated restrictions are described later in this document. As with code running at hardware interrupt level, you should make every attempt to minimize the amount of time you spend at native hardware interrupt level. If you need to do extended processing in response to a native hardware interrupt, you should schedule a secondary interrupt (using Is paging safe?Paging is not safe at native hardware interrupt level. Secondary InterruptWhat is it?The native driver model provides secondary interrupts -- which are much like deferred tasks -- allowing native drivers to defer complex processing in order to minimize interrupt latency. How do you get there?You can get to secondary interrupt level by having your native hardware interrupt handler call You can also execute a secondary interrupt handler directly from task level using What can you do there?Secondary interrupts are considered "interrupt time" as defined by the toolbox, Virtual Memory Manager and Open Transport. The associated restrictions are described later in this document. Is paging safe?Paging is not safe at secondary interrupt level. TaskWhat is it?Under the traditional Mac OS, the native driver model defines task level to be any code that's not at native hardware interrupt level and not at secondary interrupt level. How do you get there?The most common source of task level execution is standard system task level execution, i.e., normal application code. However, other execution levels that are traditionally considered to be interrupt levels, such as non-native hardware interrupts and deferred tasks, are also considered to be task level. Remember that under the traditional Mac OS, task level is defined as either non- native interrupt level or secondary interrupt level. What can you do there?The environment restrictions of task level are defined by the underlying execution level that's really being executed. Is paging safe?The native driver model defines that paging is always safe at task level. However, on the traditional Mac OS, paging is only safe at task level if the underlying execution level defines it to be safe. Software InterruptWhat is it?The native driver model defines the concept of a software interrupt, the ability to force a task to immediately execute a routine in the context of that task. This is distinct from, but commonly confused with, secondary interrupt level. How do you get there?Software interrupts are not supported under Mac OS. This is clearly stated in Designing PCI Cards and Drivers for Power Macintosh Computers, page 262:
This means is that if you call What can you do there?Software interrupts are defined to run at task level, in the context of the task to which the software interrupt was sent. Is paging safe?The native driver model defines that paging is always safe at software interrupt level.
|
Execution Levels in Other DocumentationIn general, the following execution levels are considered to be "interrupt time."
However, the use of the term "interrupt time" can vary from manager to manager. This section documents some of the more confusing cases. ToolboxMost toolbox routines cannot be called at "interrupt time," as it is defined above. There are many different reasons why toolbox routines cannot be called at interrupt time. Some routines, like all of the Memory Manager, rely on global data structures that are not interrupt-safe. Other routines might move or purge unlocked handles, which is equivalent to calling the Memory Manager. Still others, like synchronous calls to the File Manager, are architecturally inaccessible. Finally, some routines, like The fact that a routine doesn't move or purge memory does not mean it is interrupt-safe. Virtual Memory ManagerThe Virtual Memory Manager documentation ( chapter 3 of Inside Macintosh: Memory and Technote ME 09: "Coping with VM and Memory Mappings") says that page faults are not allowed at "interrupt time." This has caused a lot of confusion among programmers who have heard that, for example, Device Manager completion routines are "interrupt time," and hence assume that paging is unsafe in MacTCP completion routines. In the light of the above description, it's easy to clear up that confusion. As far as the Virtual Memory Memory is concerned, "interrupt time" means any hardware interrupt that hasn't been deferred by VM itself or using For the full story about virtual memory on the traditional Mac OS, check out Technote 1094: "Virtual Memory Application Compatibility". Open TransportThe original Open Transport documentation caused much confusion by saying that Open Transport could not be called at "interrupt time." This means that you can only call Open Transport from system task level or deferred task level. So you can call Open Transport at execution levels that would normally be considered "interrupt time" (specifically, from a deferred task) as long as you don't call it from hardware interrupt level (or native hardware or secondary interrupt levels). This confusion has been cleared up in the latest release of Inside Macintosh: Networking with Open Transport, which has an extensive table of which Open Transport routines can be called from which execution levels. |
What Interrupt Routines Can't DoCode running at "interrupt time" cannot do everything that system task code can do. The following list summarizes the operations that interrupt routines should not perform. An interrupt routine which violates any of these rules may cause a system crash:
|
Interrupt-Safe Routines by ManagerThis section describes various interrupt-safe routines, grouped by manager.
Memory ManagerThere are very few Memory Manager routines that you can safely call at interrupt time. The most common exceptions are There are some routines documented in Inside Macintosh: Memory that are safe. The entire suite of debugger routines are interrupt-safe. This includes The Virtual Memory Manager routines The Virtual Memory Manager routines No other Memory Manager routines are interrupt-safe, for one or more of the following reasons:
Specifically, do not call
Operating System UtilitiesEnqueue and Dequeue are interrupt-safe, and may be used at any time. FormatRecToString (formerly Format2Str), StringToExtended (formerly FormatX2Str), and ExtendedToString (formerly FormatStr2X) are interrupt-safe as well.
Device ManagerThe core Device Manager traps (
The next section gives details on File Manager routines that are not shared with Device Manager. If you're patching the Device Manager traps described above, you must ensure that your patch correctly handles interrupt-time requests. Your patch should not do interrupt-unsafe things unless it determines that the request is synchronous. When implementing a device driver, you receive three types of requests: synchronous, asynchronous, and immediate. If the driver can be called asynchronously, you must implement both synchronous and asynchronous requests as if they were asynchronous, and not do things that are illegal at interrupt time. [This point is discussed in great detail in Technote 1067: "Traditional Device Drivers: Sync or Swim".] On the other hand, immediate requests always execute at the execution level at which the request was made, so if you know that your client made the request at system task time, you know you are running at system task time. As a special case of this last point, a driver is always sent File ManagerAll asynchronous File Manager routines are interrupt-safe. For example, File System ManagerThe File System Manager service routines A File System Manager plug-in should assume that it is running at interrupt time, and not violate the provisions of this Technote except where noted in the File System Manager documentation. As a consequence, most File System Manager utility routines must be interrupt-safe. The routines documented not to be interrupt-safe are Driver ServicesThe native driver support library ( When reading this table, you should note a number of important caveats:
In addition, the valid execution levels for Classic NetworkingClassic AppleTalk is implemented as a set of device drivers, and hence may be called at interrupt time as long as the calls are made asynchronously. MacTCP is split into two parts. The core TCP, UDP, and ICMP support is implemented as a device driver, and hence may be called at interrupt time as long as the calls are made asynchronously. On the other hand, the Domain Name Resolver (DNR) is implemented as glue in your application. The Open TransportThe latest release of Inside Macintosh: Networking with Open Transport has an extensive table of which Open Transport routines can be called from which execution levels. Power ManagerInstalling and removing a sleep queue entry (using SleepQInstall and SleepQRemove) is safe, as are BatteryStatus and SetWUTime.
Notification ManagerYou may call
Desktop ManagerAll asynchronous Desktop Manager routines are interrupt-safe. For example, the |
IMPORTANT: |
GetFrontProcess, GetCurrentProcess, GetNextProcess, SameProcess, and WakeUpProcess are interrupt-safe.
InsTime, InsXTime, PrimeTime, and RmvTime are interrupt-safe.
All asynchronous PPC Toolbox routines are interrupt-safe.
Deferred task installation via DTInstall
is interrupt-safe. A deferred task runs at interrupt time with respect to most of the Mac OS toolbox and should follow the rules for interrupt time code.
SlotVInstall, VRemove, SlotVRemove, AttachVBL, DoVBLTask, and GetVBLQHdr are all interrupt-safe.
SetupA5
, SetupA4
, SetCurrentA5
, SetCurrentA4
, and so on are interrupt-safe as long as the implementations do not reside in an unloaded segment. You should check the code generated by your development environment before using such routines at interrupt time.
Anything in PLStringFuncs.h
is safe, as long as the implementations do not reside in an unloaded segment.
Do not call any routine implemented in a package (List Manager, Disk Initialization, Standard File, SANE, International Utilities, AppleEvent Manager, PPC Browser, Edition Manager, Color Picker, Database Access Manager, Help Manager, and the Picture Utilities) at interrupt time. Package routines are not interrupt-safe, since the package may not be in memory at that time.
Opening and closing a component is not safe to do at interrupt time, but many other component routines are interrupt safe. You should check the specifics of the component in question to determine exactly which functions can be called at interrupt time.
The only interrupt-safe Event Manager routines are PostEvent
and PPostEvent
. Other routines, specifically TickCount
, are not interrupt-safe.
IMPORTANT: |
Virtually none of QuickDraw is interrupt-safe. The exception is SetCursor
, which is documented as interrupt-safe. If you patch SetCursor
, you should be sure that your patch is interrupt-safe because it can and will be called at interrupt time.
IMPORTANT: Apple is aware of the demand for an interrupt-safe mechanism for setting color cursors and is working on an alternate mechanism. |
Do not be tricked into thinking that trivial QuickDraw routines -- such as SetRect
or Random
-- are interrupt-safe: they are not! This is partly by definition and partly because it's possible for these routines to reside in pageable code fragments. If you call these routines at any time paging is unsafe, they could cause a fatal page fault.
Summary of Interrupt-Safe RoutinesThis is a summary list of routines which may be called at interrupt time. Those routines with an asterisk (*) have restrictions on their use; see the main body of this Technote for details: AddrToName * AttachVBL BatteryStatus BlockMove PBControlAsync DebuggerEnter DebuggerExit DebuggerGetMax DebuggerLockMemory DebuggerPoll DebuggerUnlockMemory DeferUserFN Dequeue DoVBLTask Enqueue ExtendedToString Format2Str FormatRecToString FormatStr2X FormatX2Str GetCurrentProcess GetFrontProcess GetFSInfo GetNextProcess GetPageState GetPhysical GetVBLQHdr HInfo * HoldMemory * InsTime InsXTime LockMemory * LockMemoryContiguous * LockMemoryForOutput * MACEVersion MXInfo * NMInstall NMRemove Open Transport routines * PBAllocContigAsync PBAllocateAsync PBCatMoveAsync PBCatSearchAsync PBCloseAsync * PBCloseWDAsync PBControlAsync PBControlImmed * PBCreateAsync PBCreateFileIDRefAsync PBDTAddAPPLAsync PBDTAddIconAsync PBDTDeleteAsync PBDTFlushAsync PBDTGetAPPLAsync PBDTGetCommentAsync PBDTGetIconAsync PBDTGetIconInfoAsync PBDTGetInfoAsync PBDTRemoveAPPLAsync PBDTRemoveCommentAsync PBDTResetAsync PBDTSetCommentAsync PBDeleteAsync PBDeleteFileIDRefAsync PBDirCreateAsync PBExchangeFilesAsync PBFlushFileAsync PBFlushVolAsync PBGetAltAccessAsync PBGetCatInfoAsync PBGetEOFAsync PBGetFCBInfoAsync PBGetFInfoAsync PBGetFPosAsync PBGetForeignPrivsAsync PBGetUGEntryAsync PBGetVInfoAsync PBGetVolAsync PBGetWDInfoAsync PBGetXCatInfoAsync PBHCopyFileAsync PBHCreateAsync PBHDeleteAsync PBHGetDirAccessAsync PBHGetFInfoAsync PBHGetLogInInfoAsync PBHGetVInfoAsync PBHGetVolAsync PBHGetVolParmsAsync PBHMapIDAsync PBHMapNameAsync PBHMoveRenameAsync PBHOpenAsync * PBHOpenDFAsync PBHOpenDenyAsync PBHOpenRFAsync PBHOpenRFDenyAsync PBHRenameAsync PBHRstFLockAsync PBHSetDirAccessAsync PBHSetFInfoAsync PBHSetFLockAsync PBHSetVolAsync PBLockRangeAsync PBMakeFSSpecAsync PBOpenAsync * PBOpenDFAsync PBOpenRFAsync PBOpenWDAsync PBReadAsync PBReadImmed * PBRenameAsync PBResolveFileIDRefAsync PBRstFLockAsync PBSetAltAccessAsync PBSetCatInfoAsync PBSetEOFAsync PBSetFInfoAsync PBSetFLockAsync PBSetFPosAsync PBSetFVersAsync PBSetForeignPrivsAsync PBSetVInfoAsync PBSetVolAsync PBStatusAsync PBStatusImmed * PBShareAsync PBUnlockRangeAsync PBUnshareAsync PBWriteAsync PBWriteImmed * PBXGetVolInfoAsync PageFaultFatal PostEvent PPostEvent PrimeTime RmvTime SameProcess SetCursor SetFSInfo SetWUTime SleepQInstall SleepQRemove SlotVInstall SlotVRemove SndDoCommand * SndGetSysBeepState SndManagerStatus SndPauseFilePlay SndSetSysBeepState SndSoundManagerVersion PBStatusAsync StrToAddr * StringToExtended StripAddress SwapMMUMode Translate24to32 UnholdMemory UnlockMemory UTAllocateFCB UTReleaseFCB UTLocateFCB UTLocateNextFCB UTIndexFCB UTResolveFCB UTAddNewVCB UTLocateVCBByRefNum UTLocateVCBByName UTLocateNextVCB UTAllocateWDCB UTReleaseWDCB UTResolveWDCB UTFindDrive UTAdjustEOF UTSetDefaultVol UTGetDefaultVol UTEjectVol UTCheckWDRefNum UTCheckFileRefNum UTCheckVolRefNum UTCheckPermission UTCheckVolOffline UTCheckVolModifiable UTCheckFileModifiable UTCheckDirBusy UTParsePathname UTGetPathComponentName UTDetermineVol UTGetBlock UTReleaseBlock UTFlushCache UTMarkDirty UTTrashVolBlocks UTTrashFileBlocks UTTrashBlocks UTCacheReadIP UTCacheWriteIP UTBlockInFQHashP UTVolCacheReadIP UTVolCacheWriteIP VRemove WakeUpProcess |
Thanks to Tim Carroll, Mark Cookson, Cameron Esfahani, Pete Gontier, Rich Kubota, Steve Lemke, Peter N. Lewis, Jim Luther, Dave Lyons, Matt Mora, and Jim Murphy.
Technotes
Previous Technote | Contents | Next Technote