Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Networking With Open Transport / Part 1 - Open Transport Essentials
Chapter 5 - Programming With Open Transport


Interrupt Levels and Open Transport Processing

The Open Transport API offers a set of functions that you can use to schedule code to run at system task level, at deferred task level, and, in some cases, at hardware interrupt level. This section briefly describes the Mac OS interrupt levels, lists the restrictions on code executing at each level, and explains how you should use the functions provided by Open Transport to schedule code to run at these levels. This information is important in understanding the synchronization problems that might arise during asynchronous processing--where interaction between code executing at different levels might cause unexpected behavior.

Hardware Interrupt Level

Hardware interrupt-level execution happens as a result of a hardware interrupt request. Installable interrupt handlers for PCI bus, NuBus and other devices, as well as interrupt handlers supplied by Apple all execute at this level.

In general, you should minimize the amount of time that your code spends executing at hardware interrupt level. If you think you need to do extended processing at this level, you should consider trying to defer such processing to deferred task level. For information about how you do this, see "Deferred Task Level".

If virtual memory is on, paging is not safe at hardware interrupt level unless the interrupt has been postponed using the Memory Management function DeferUserfn. Some system interrupt handlers (Device Manager completion routines, VBLs, slot VBLs, Time Manager tasks) automatically defer their operation to a safe time, but other hardware interrupt handlers must be sure not to cause page faults.

Open Transport furnishes a number of utility functions that you are allowed to call at hardware interrupt time. Appendix C lists these functions. In some cases, you must notify Open Transport that you are about to call an Open Transport function at hardware interrupt time by first calling the OTEnterInterrupt function. You can then call one of the permitted functions. When you are done with calling Open Transport functions at hardware interrupt time, you must call the OTLeaveInterrupt function. For example, you could execute these code statements in this sequence:

OTEnterInterrupt();
OTScheduleDeferredTask(dtCookie);
OTLeaveInterrupt();
WARNING
If you try to call an Open Transport function that is not permitted at interrupt time or if you do not use the OTEnterInterrupt and OTLeaveInterrupt functions when these are required, you will either get the OTBadSyncErr result code or crash your system, depending on the function you call.

Deferred Task Level

A deferred task is the means whereby you can schedule a routine to be executed from hardware interrupt level code. Deferred task processing occurs just before the operating system returns from hardware interrupt level to system task time. Scheduling code to run at deferred task time minimizes the time that code executes at hardware interrupt level and therefore minimizes system interrupt latency. Deferred tasks are executed serially, offering a simple mutual exclusion mechanism.

Programs using Open Transport can cause code to run at deferred task time by creating a deferred task with the OTCreateDeferredTask function and by scheduling it to run using the OTScheduleDeferredTask function. Using Open Transport functions to create and schedule deferred tasks is preferable to using the Deferred Task Manager function DTInstall, because by doing so you allow Open Transport to adapt to changes in the underlying operating system without having to change your code.

Code also executes at deferred task time if it is called by something that is executing at deferred task level. For example, Open Transport often calls notifier functions at deferred task level. You should assume, in writing your notifier functions, that they are likely to run at deferred task level and observe the restrictions on code running at this level.

If you are writing a system extension or a code resource, you probably need to use Open Transport's deferred task functions to get processing time to handle such tasks as allocating memory or accessing disk space. You must schedule a deferred task if you want to call such code from code that executes at interrupt time or from within an interrupt function such as a Time Manager function, Vertical Retrace Manager function, File Manager completion routine, or Device Manager completion routine.

Virtual Memory paging is safe at deferred task level. You can also call many Open Transport functions at deferred task time; these functions are described in Appendix C.

IMPORTANT
If you are writing a PCI device driver (ndrv), please note that Open Transport treats secondary interrupt level as hardware interrupt level. Therefore, your secondary interrupt handler is subject to the same restrictions as code running at hardware interrupt time, as described in the previous section.

System Task Level

System task level is the level at which most application code executes. An application's main entry point is called at system task level. Cooperatively scheduled Thread Manager threads also run at system task time.

Open Transport furnishes several functions that you can use to schedule code to execute at system task level. Normally, you don't need to use these functions because your application executes within a normal event loop that runs at system task level. However, you might want to use Open Transport's system task scheduling functions for some of your application's processing because these functions provide an efficient way to streamline your main event loop. For example, you can avoid handling some of your memory allocation during your main event loop; instead, you can schedule a system task to obtain memory at certain times or on a periodic basis.

System task level is not considered interrupt level by any part of the system. Consequently, you can call anything at system task level. Virtual Memory paging is also safe at this level unless your code accesses some resource that the system needs to support paging. For example, if you get exclusive access to the SCSI bus by calling the function SCSIGet, you must not cause a page fault even at system task level.

Using Timer Tasks

Open Transport provides functions that you can use to create a timer task, to schedule the task, to cancel it, and to dispose of it. These functions are described in "Working With Timer Tasks". Open Transport executes timer tasks at deferred task time.

IMPORTANT
You cannot call these functions from 68000 code running on a Power PC.

Using System and Deferred Tasks

You can use Open Transport functions to schedule a callback function that will be called at system task time or deferred task time. To do this, you use the function OTCreateSystemTask or the function OTCreateDeferredTask to create the task. Then you use the function OTScheduleSystemTask or the function OTScheduleDeferredTask to schedule the task.

The OTCreateSystemTask and OTCreateDeferredTask functions allocate a structure that defines the task you want executed. Upon completion, these functions return a reference by which you subsequently refer to the task when scheduling, cancelling, or destroying the task. When you create the task, you can also specify user-defined context information that Open Transport will pass to your task when it calls it. For 680x0 code, Open Transport also restores the A5 world to what it was when you created the task.

Calling Open Transport Functions

Appendix C includes a table that lists all the Open Transport functions you can call at deferred task time. In general, you can make all endpoint calls from a deferred task as long as the endpoint is in asynchronous mode. A select number of Open Transport calls can only be made at system task time.

IMPORTANT
Because opening the first endpoint for a configuration requires that Open Transport load libraries, doing this from a deferred task will only work if the foreground task is calling the functions WaitNextEvent, GetNextEvent, or SystemTask. Subsequent asynchronous open calls from a deferred task will work regardless of what the foreground task is doing because the libraries will have already been loaded.

Scheduling Tasks

Once you have created a task, you need to schedule it for execution. To do this, you use the functions OTScheduleSystemTask, OTScheduleDeferredTask, or OTScheduleInterruptTask. You pass the task reference (using the stCookie or the dtCookie parameter) to the function, and Open Transport attempts to schedule the task. If a system task is scheduled successfully, it executes when the SystemTask function next executes. If a deferred task is scheduled successfully, it executes as soon as possible after hardware interrupts have finished executing.

Because a system task can happen relatively slowly, enough time can elapse between scheduling and execution to let you cancel the task before it runs. If you use the OTCancelSystemTask function, you notify Open Transport not to execute the system task at the scheduled time. The reference remains valid, and you can choose to reschedule the task by using the OTScheduleSystemTask function again at any time. Deferred tasks, however, typically execute too quickly to allow time for canceling them.

You can also choose to reschedule a system or deferred task after it has executed successfully. You do this by using the OTScheduleSystemTask or the OTScheduleDeferredTask function again at any time. If you choose to reschedule a task, you reuse the same reference. This means that exactly the same task executes, which is useful for repetitive periodic tasks.

Deallocating Resources

You can destroy a task with the OTDestroySystemTask or the OTDestroyDeferredTask functions. These functions make the task reference invalid and free any resources associated with the task. You can call these functions whenever it is no longer necessary to schedule a task, such as when it has been executed at its scheduled time and you have no plans to reschedule it for later use.

You can call the OTDestroySystemTask function to destroy a system task that is currently scheduled for execution. In this case, Open Transport cancels the system task before proceeding with the task's destruction.

If you want to use a task after you have destroyed it, you must begin again by creating a new task with the OTCreateSystemTask or the OTCreateDeferredTask functions.

Handling Synchronization Problems

If you call certain Open Transport functions from different interrupt levels, synchronization problems can occur. For example,

  1. You call the function OTRcv from your main thread.

  2. There is no pending data; just as the function is about to return to the application with the result kOTNoDataErr, an inbound data packet interrupts Open Transport, and it steps up to deferred task time to process the data.

  3. Open Transport calls your notifier with a T_DATA event, which you ignore because you are not aware of the possibility that the execution of the OTRcv function could be interrupted by the actual arrival of data (processed in a different interrupt context).

  4. The call to OTRcv in your main thread completes with the result kOTNoDataErr, you have no way of knowing that you got the T_DATA event, and you won't get another one until you call the function again, for another kOTNoDataErr result. Consequently, your application hangs.

The solution to this problem is to adopt a sensible synchronization model--that is, do everything in your notifier (using the OTEnterNotifier function when you can't) or do everything at system task time. The key is not to mix and match execution levels for the same endpoint.


Subtopics
Hardware Interrupt Level
Deferred Task Level
System Task Level
Using Timer Tasks
Using System and Deferred Tasks
Calling Open Transport Functions
Scheduling Tasks
Deallocating Resources
Handling Synchronization Problems

Previous Book Contents Book Index Next

© Apple Computer, Inc.
15 JAN 1998