Important: The information in this document is obsolete and should not be used for new development.
About the Deferred Task Manager
Every type of interrupt has an interrupt priority level, a number that identifies the importance of the interrupt. The microprocessor also maintains several bits in the status register of the CPU that indicate which interrupts are currently to be processed and which are to be ignored. This processor priority is always set to the interrupt priority level of the highest-priority interrupt currently executing. For example, if no interrupts are being serviced, the processor priority is 0. If the current application is then interrupted by a vertical retrace interrupt, the interrupt priority is set to 1 during the servicing of the interrupt and restored to 0 upon completion. If, during the servicing of the vertical retrace interrupt, a level-2 interrupt occurs, the processor priority is set to 2 during the servicing of the interrupt and restored to 1 upon completion of any level-2 interrupt tasks.The microprocessor ordinarily services an interrupt only if its interrupt priority level is higher than the processor priority. Accordingly, when no interrupt routines are executing, the microprocessor can service any new interrupt. If, however, a slot interrupt is executing, the microprocessor ignores other slot interrupts and interrupts of lower priority. As a result, a lower-priority interrupt (for example, a vertical retrace interrupt) might not execute on schedule.
When the microprocessor is servicing one interrupt, it is said to disable other interrupts whose priority level is lower than or the same as that of the interrupt being serviced. This feature prevents the interruption of tasks by interrupts of lesser or equal priority. You might, however, initiate an interrupt task that does not need this extra protection. If an interrupt task takes so much time to execute that the disabling of other interrupts during execution becomes significant, you might prefer it to have your interrupt task executed at a time when all other interrupt tasks have been serviced and interrupts are reenabled. The Deferred Task Manager provides a mechanism for this purpose.
Instead of immediately performing the main work of a task, such as a slot-interrupt task, you can defer the task, or schedule it for execution when all interrupts have been reenabled. You do this by placing information about the task to be deferred in a deferred task record, which you then insert in the deferred task queue. The task is then known as a deferred task. All system interrupt handlers check the deferred task queue just before returning. If there are tasks in the queue and the microprocessor's status register is about to be reset to 0, the system interrupt handlers reenable interrupts and pass control to the Deferred Task Manager to execute all the deferred tasks.
The Deferred Task Manager checks whether a VBL task is active. If so, the Deferred Task Manager exits, and the deferred tasks remain deferred until the VBL task completes. (The VBL task is interrupt code, and so the Deferred Task Manager is called again when the Vertical Retrace Manager returns control to the primary interrupt handler.) If a VBL task is not active, the Deferred Task Manager checks whether a deferred task is already active. If so, the Deferred Task Manager exits. Otherwise, a deferred task is removed from the queue and executed. When all deferred tasks have been removed from the queue and executed, the Deferred Task Manager returns control to the primary interrupt handler.
Each interrupt task is removed from the deferred task queue before it is executed. For this reason, your interrupt code must reinstall the task record into the queue each time the task is to be deferred. If your task is simple enough that reinstalling the task record into the deferred task queue takes about as much time as doing the real work of the task, then the Deferred Task Manager is not useful for your application. Note that interrupts are disabled during the reinstallation of a task record into the deferred task queue, even though they are reenabled before the reinstalled task is executed.
Although you can use the Deferred Task Manager for all types of interrupt tasks, it is especially convenient for slot-interrupt tasks. Interrupts from NuBus
\x89 slot devices are received and decoded by special hardware on the main logic board. This hardware generates level-2 interrupts. Because of the way the hardware works, the microprocessor must disable lower-priority interrupts until it services the level-2 interrupts (otherwise, a system error occurs). During the execution of slot-interrupt tasks, the microprocessor disables other level-2 interrupts, such as those for sound, as well as all level-1 interrupts. By using the Deferred Task Manager, you can defer the processing of slot interrupts until all of the slots are scanned. Just before returning, the slot-interrupt handler executes any tasks having records in the deferred task queue. It is important to remember that deferred tasks are executed at the end of a hardware interrupt cycle, before the secondary interrupt handler returns. In addition, the tasks in the deferred task queue are executed only if the status register is being restored to 0 (that is, all interrupts reenabled). If the status register is not being restored to 0, but only to some higher level, deferred tasks are not executed during that hardware interrupt cycle.
This behavior, if not properly understood, can lead to some puzzling situations. For example, applications can mask the CPU's status register to disable certain interrupts. Suppose that your application installs and activates a Time Manager task, which is triggered by level-2 interrupts. If you don't want the task to be executed during a specific period of time, you can set the status register to 2, thus disabling all level-1 and level-2 interrupts. (In this case, the status register is set to 2, but not in response to a level-2 interrupt.)
Now suppose that a level-4 interrupt occurs, perhaps triggered by the arrival of some LocalTalk data at a serial port. The LocalTalk interrupt handler is executed with the status register set to 4. That handler might install a deferred task and then return. Because the interrupt cycle is nearly complete, the system interrupt handler checks whether the status register is about to be restored to 0. In the situation described, the status register is about to be restored to 2, not to 0. As a result, any pending deferred tasks, including the newly installed LocalTalk deferred task, are ignored. Moreover, if the status register remains masked at 2, any additional deferred tasks installed by the LocalTalk interrupt handler remains queued and are not executed.
Eventually, the application that masked the status register (to disable its Time Manager task) will restore the status register to 0. At the end of the next hardware interrupt cycle, all the pending deferred tasks are finally executed.
As you can see, it's possible for an interrupt routine--in this example, the LocalTalk interrupt handler--to install a deferred task that is not executed until after some future hardware interrupt cycle. Indeed, that future hardware interrupt might well be another LocalTalk interrupt. In other words, it's possible for an interrupt routine to install a deferred task and to be called again, before the deferred task has been executed. It's even possible for the interrupt routine to interrupt the deferred task that it installed during some previous interrupt cycle. You need to make sure, for instance, that your interrupt code doesn't modify a data buffer that a deferred task is processing.
Keep these points in mind when you use the Deferred Task Manager to defer tasks:
- The purpose of the Deferred Task Manager is to allow lengthy interrupt tasks to be deferred until all interrupts can be reenabled.
- Deferred tasks are executed with all interrupts enabled (that is, with the status register set to 0).
- Deferred tasks are not executed if some other interrupt code is executing. For example, a deferred task will not interrupt a VBL task.
- A deferred task is not executed if some other deferred task is being executed. A deferred task cannot interrupt another deferred task.
- Deferred tasks can be interrupted.
- Deferred tasks are executed within the hardware interrupt cycle, even though the status register is set to 0 before the tasks are executed. As a result, deferred tasks are subject to all the normal limitations on interrupt-level code. In particular, deferred tasks cannot call any routine that directly or indirectly allocates or moves memory, and cannot depend on the validity of unlocked handles. See the chapter "Introduction to Processes and Tasks" in this book for a complete description of these limitations.
- Deferred tasks are not prioritized. They are executed in the order they were added to the deferred task queue, no matter what interrupt level the code that installed them was running at.