ADC Home > Reference Library > Technical Notes > Legacy Documents > Hardware & Drivers >
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:
|
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 that 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 Considerations Another 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: Currently,
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 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. Determining the Execution LevelThere is no good general purpose way to determine the current execution level. In general, your code must know in advance the level at which it is executing. However, there are a number of mechanisms that work for specific environments. The following sections describe those mechanisms and their flaws. Tracking Interrupts YourselfOne solution for determining the current execution level is to track interrupts yourself. The code in Listing 1 outlines how you can do this.
This technique works well if you know the execution level of all entry points to your code. However, it fails when your code can be called at unknown execution levels. For example, a disk driver's Prime entry point can be called at a variety of execution levels. Testing the Interrupt MaskA common mistake made by developers is to assume that a non-zero 68K interrupt mask (bits 8 through 10 of the 680x0 SR register) indicates that the processor is running at 'interrupt time'. This is an incorrect assumption. It provides both false negatives and false positives.
Open TransportOpen Transport provides three
routines,
As an example of where these
routines go wrong, if you call
The upshot of this is that these Open Transport routines are helpful for OT programmers, but do not solve the problem in general. CurrentExecutionLevelDriverServicesLib, introduced with
the first PCI Power Macintosh computers, exports a routine
called As originally implemented,
This algorithm works fine within
the execution levels supported by DriverServicesLib, but it
does not account for execution levels outside of the native
driver model. For example, if you call this version of
In recent systems
Therefore
TaskLevelMac OS 9.0 introduced a new system
routine, Listing 4.
The idea behind
Listing 5. An
example of using
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 Utilities
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
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.
The existing documentation (Adding
Multitasking to Applications Using Multiprocessing
Services, version 2.1) mentions that the following
routines are interrupt safe: MPCurrentTaskID
,
MPYield
, UpTime
,
MPSignalSemaphore
, MPSetEvent
, and
MPNotifyQueue
.
IMPORTANT: |
In addition, the routines
MPTaskIsPreemptive
, MPBlockCopy
,
MPBlockClear
, and MPDataToCode
are
interrupt safe, even though the existing documentation does
not say that they are. This change will be rolled into a
future version of the documentation. [2456896]
InsTime
, InsXTime
, PrimeTime
,
and RmvTime
are interrupt safe.
All asynchronous PPC Toolbox routines are interrupt safe.
The Connection Manager routines CMRead
,
CMWrite
, and CMStatus
are
interrupt safe; all other Connection Manager, Terminal
Manager, File Transfer Manager, Communications Resource
Manager, and Communications Toolbox Utilities routines are
not.
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, Apple Event 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 OSEventAvail
,
TickCount
and GetKeys
, are not
interrupt safe.
IMPORTANT:
If you are writing interrupt-time code, you should use the alternatives shown in the Table 1. |
Table 1. Interrupt-safe
alternatives to TickCount
and
GetKeys
.
Routine |
Traditional Mac OS |
Carbon |
---|---|---|
|
|
|
|
|
|
|
|
none/ |
Notes:
TickCount
on traditional Mac OS
calls LMGetTicks
and is therefore
interrupt safe.GetKeys
is interrupt-safe on traditional Mac OS when
using CarbonLib 1.1 or later. It is always
interrupt-safe on Mac OS X.KeyMap
is a low-memory global
(at location $174) which contains the data
returned by GetKeys
.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.
EqualString
and RelString
are
interrupt safe, along with any other routines based on the
_CmpString
($A03C) and _RelString
($A050) traps. These routines must be interrupt safe because
they are used by parts of Mac OS (for example, File Manager
and classic AppleTalk) that execute at interrupt time.
IMPORTANT: |
Note:
|
It is possible, with some restrictions, to call the Unicode Converter at interrupt time. If you have a specific product that needs this ability, please contact DTS for details.
Table 1 contains a listing 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:
Table 1. Listing of Interrupt-Safe Routines.
|
|
Designing PCI Cards and Drivers for Power Macintosh Computers
Technote 1033: "Interrupts in Need of (a Good) Time"
Technote 1067: "Traditional Device Drivers: Sync or Swim"
Technote 1084: "Running CFM-68K Code at Interrupt Time: Is Your Code at Risk"
Technote 1094: "Virtual Memory Application Compatibility".
Technote ME 09: "Coping with VM and Memory Mappings
February 1998 |
Originally written. |
July 1998 |
Updated with new and revised material:
|
November 1999 |
Updated with new material in the Event
Manager section to list
|
December 1999 |
Updated to add the Text Utilities section. |
January 2000 |
Updated to add the Unicode
Converter section and correct the discussion on
|
February 2000 |
Updated to discuss Communications Toolbox. |
April 2000 |
Updated to discuss Multiprocessing Services. |
October 2000 |
Updated to discuss
mechanisms used to determine
the current execution
level. Updated the
Event
Manager section to
document that |
|
Acrobat version of this Note (172K) |
|
|
New CurrentExecutionLevel detection code |
|