| 
	
		|  | 
This technote discusses a number of Time Manager issues
that are not covered in the
Time
Manager chapter of Inside Macintosh: 
Processes. This Note is intended for all developers who
want to do time measurement using the Time Manager
routines.  Updated: [Sep 01 1996] |  
 
 
 
 
 
 Some Basic Time Manager RulesWhen programming with the Time Manager, it is important
         to observe the following rules:  
            For each Time Manager task that you insert (using
            InsTimeorInsXTime), you must
            remove the same Time Manager task (usingRmvTime) once and only once.While your Time Manager task is inserted (that is,
            between InsTimeandRmvTime),
            you can prime the task multiple times. For example, the
            sequenceInsTime,PrimeTime,
            fire,PrimeTime, fire,PrimeTime, fire,RmvTimeis
            perfectly legal. Some developers always insert, prime and
            remove their Time Manager tasks. While legal, this is
            unnecessarily inefficient.If you remove a Time Manager task (using
            RmvTime), you must insert it again (usingInsTimeorInsXTime) before
            priming it.If you have primed a Time Manager task, you must not
            prime it again until it fires. This is explicitly called
            out in
            Inside
            Macintosh: Processes, page 3-11. If you need to
            cancel a Time Manager task, simply remove it using
            RmvTime. If you need to reschedule a Time
            Manager task, remove it, then reinsert it, then prime it
            again.Don't use InsXTimeunless you want drift
            free timing. A common mistake is to install a drift free
            Time Manager task (usingInsXTime), prime it
            for 1 second, let it fire, and then, 5 minutes later,
            prime it again for another 1 second. The task fires
            immediately because it was installed as a drift free
            task. To avoid this behavior, simply use the originalInsTimecall. This rules are not new; they are all explicitly or
         implicitly described in Inside Macintosh: 
         Processes. However, recent systems now enforce these
         rules more strictly. If you break these rules, you may see
         one of the following symptoms:  
            The system crashes with a
            dsVMDeferredFuncTableFull(112) system
            error.The system freezes because low-memory is trashed
            (prior to System 7.5.5).The system bus errors because of a
            fatal
            page fault.PrimeTimereturnsqErr(-1)
            when you attempt to prime a task that isn't installed.Timer Manager tasks not firing at the right time. 
 
| Note:"Timer.h" defines the various Time Managers
                  routines as void functions, which prevents you
                  from getting the error result from
 PrimeTime. See
                  Time Manager
                  Errors for instructions on how to get this
                  error result. |  
 Back to top 
 Setting Up tmReservedOn page 3-8 of
         
         Inside Macintosh: Processes, it clearly states
         that both tmWakeUpandtmReservedshould be set to 0 (as shown in Listing 1) prior to the
         first call toInsXTimewhen using the extended
         Time Manager. 
	| theTMTask.tmWakeUp = 0;
theTMTask.tmReserved = 0;
InsXTime((QElemPtr)&theTMTask);
PrimeTime((QElemPtr)&theTMTask, 2000); |  
	| Listing 1. Set both
tmWakeUpandtmReservedto 0 before callingInsXTime. |  If you do want to do some time measurement, then you have
         to call RmvTimeto get the current value oftmCount, which leads later to a new call toInsXTime, and a call toPrimeTimewith a 0 delay which has a special meaning in that case.
         Although it appears, after much reading, rather clear that
         you leave the current value oftmWakeUpuntouched in theTMTaskstructure, you can't be
         sure what to do about the value oftmReserved. The truth is that prior to October, 1992 (System Software
         7.1), you didn't care, but it's more of a concern now, since
         Apple slightly modified the behavior of the Time Manager to
         deal with performance issues. If you use code like that in Listing 2 and leave
         tmReserveduntouched, then after 127 calls your
         extended time task is converted into a non-extended time
         task (for a good but can't-be-disclosed reason) which, being
         waked up with a 0 delayPrimeTime(which has no
         special meaning for a non-extended time task), will suddenly
         be called and called again -- more frequently than it should
         be. 
	| // WARNING: Don't use this code!
RmvTime((QElemPtr)&theTMTask);
remaining = theTMTask.tmCount;
InsXTime((QElemPtr)&theTMTask);
PrimeTime((QElemPtr)&theTMTask, 0);
// WARNING: Don't use this code! |  
	| Listing 2. Typical code for
measuring time using the Time Manager. |  So, if you perform that kind of time measurement, change
your code to that shown in Listing 3. 
	| RmvTime((QElemPtr)&theTMTask);
remaining = theTMTask.tmCount;
theTMTask.tmReserved = 0;
InsXTime((QElemPtr)&theTMTask);
PrimeTime((QElemPtr)&theTMTask, 0); |  
	| Listing 3. Correct code for measuring time using the Time Manager. |  Since the Time Manager, prior to System Software 7.1,
         doesn't care about tmReserved, then you can settmReservedto 0 before each call toInsXTimewithout checking the system version.
         You still have, of course, to ensure that the Time Manager
         you're using is the extended one (the response togestaltTimeMgrVersionisgestaltExtendedTimeMgr(3) or greater). Back to top 
 About tmWakeUpThe following sentence, also on page 3-8 in Inside
         Macintosh: Processes, is incorrect: "The
         tmWakeUpfield contains the time at which the
         Time Manager task specified bytmAddrwas last
         executed (or 0 if it has not yet been executed)." It should
         say: "ThetmWakeUpfield contains the time at
         which the Time Manager task specified bytmAddris scheduled to be executed (or 0 if it has not yet been
         primed)." 
 
| Note:Since the format of that field is undocumented and
                  used internally by the Time Manager, developers are
                  strongly discouraged anyway from performing any
                  kind of calculation or comparison on the value of
                  this field, since that format could change in the
                  future.
 |  
 Back to top 
 The Microseconds AlternativeAnother way to perform time measurement would be to use
         the Microsecondscall, which is much easier to
         use and less likely to change in the future. 
	| pascal void Microseconds(UnsignedWide *microseconds); |  
	| Listing 4. The prototype for Microseconds. |  
 
| Important:
 Microsecondshas a reputation for being
                  inconveniently slow. Many developers have run
                  performance tests and noticed thatMicrosecondscan take over 10 microseconds to
                  execute. Prior to the advent of the native
                  Time Manager,Microsecondshad a number of performance
                  problems: 
                     It was implemented in
                     68K code.It relied on a time
                     base which is very slow to access on modern
                     computers.The algorithm was
                     somewhat convoluted.It was patched by
                     various system components. The native Time Manager
                  has significantly improved the performance of
                  Microseconds. However, for best performance,
                  your application should use theUpTimeroutine.UpTimeis layered directly on top of the
                  PowerPC Time Base Register (TBR) and is both faster
                  and more accurate thanMicroseconds. UpTimeis exported byInterfaceLibon all computers running Mac OS
                  8.5 and higher, and byDriverServicesLibon all PCI-based computers
                  regardless of the system version.
 |  
 Back to top 
 Undeferred Time Manager TasksThis section describes an optimization that you might
         want to employ when using the Time Manager in the presence
         of virtual memory (VM). Most developers will not be
         interested in this; however, all users of the Time Manager
         should heed the following warning. 
            
               | WARNING: Because there is an extremely remote
                  possibility that the memory you have allocated for
                  your Time Manager task contains the special value
                  listed below, if you want to ensure the behavior
                  defined in
                  
                  Inside Macintosh: Memory, you should always
                  clear the
 qLinkfield in theTMTaskbefore installing it. |  
 As described in
         Inside
         Macintosh: Memory, Time Manager tasks are
         automatically deferred by the Virtual Memory (VM) system to
         avoid fatal page faults. This was done for backward
         compatibility with existing applications that use the Time
         Manager, but it can seriously increase the latency between
         when the timer expires and when your Time Manager task
         executes. For more information about interactions between the Time
         Manager and VM, see Technote 1094
         Virtual Memory Application
         Compatibility. For example, if you schedule a Time Manager task to
         execute at time X and, at time (X - delta) some process
         takes a page fault, your Time Manager task will not be
         called until time (X + Y - delta), where Y is the time
         required to field a page fault. If the page fault causes the
         hard disk to seek, Y could be as great as the hard disk's
         average seek time, approximately 10 ms. If you are trying to
         use the Time Manager to measure time in
         microseconds, this could be a problem. There is a way you can install Time Manager tasks so the
         callback is not deferred by VM; however, before using this
         technique, you should be aware of its dangers. Because VM
         does not defer these special Time Manager tasks, it is
         possible for them to fire when paging is not safe. To avoid
         fatal page faults, you must ensure:  
            The TMTaskrecord is held for the entire
            time the Time Manager task is installed (see Listing 5).The code for the timer task and any data it
            references is held. If the code for your timer task is
            stored in a code resource, you can use the snippet from
            Listing 6 to make sure it is held. If your timer task
            code is not in a code resource, it's very difficult to
            ensure that it and its data are held.You timer task code only calls system routines that
            are guaranteed to meet the above requirement -- this
            typically means only that routines that are known to be
            interrupt-safe. 
	| HoldMemory(&theTask, sizeof(TMTask)); |  
	| Listing 5. Holding the Time Managertask itself. |  
	| // Ensure the code doesn't move in logical memory
HLock(ttaskCodeHandle);
// Ensure the code is held in physical memory and cannot be paged to disk
HoldMemory(*ttaskCodeHandle, GetResourceSizeOnDisk(ttaskCodeHandle)); |  
	| Listing 6. A source code listing showing some code that helps to explain my idea.... |  
 
| WARNING:If you fail to meet these requirements, you will
cause a fatal page fault and crash the system.
 |  
 If you call InsTimeorInsXTimewith theqLinkfield set to $65616461, the VM
         patch on the Time Manager will recognize your special
         requirements and execute your timer task as soon as it
         fires, regardless of whether paging is safe or not. Back to top 
 The Native Time Manager
         In late 1999, Apple introduced a new
         implementation of the Time Manager, known as the native Time
         Manager. The native Time Manager has the following
         features:  
            It publishes the same
            programming interface as the extended Time
            Manager.It is implemented as PowerPC
            native code.It uses the PowerPC Time Base
            Register (TBR) and Decrementer register (DEC) as its time
            source. Prior to the introduction of the native Time
            Manager, all timing was done using the timer registers in
            the VIA (6522 Versatile Interface Adapter) or, more
            accurately, the VIA cell in the system's I/O ASIC. This
            change yields both more accurate timing and faster
            execution time.The Microsecondscall is also implemented in terms of
            TBR, so the Time Manager andMicrosecondsare synchronized. The native Time Manager is
         available on all ROM-in-RAM CPUs running Mac OS 9 or later,
         and on CPUs with the Uni-N ASIC running Mac OS 8.6. The best
         way to detect the presence of the native Time Manager is to
         call Gestaltwith thegestaltTimeMgrVersionselector and look for the resultgestaltNativeTimeMgr. 
 
| Note:The definition of
 gestaltNativeTimeMgr, shown below, is missing from Universal Interfaces 3.3.2 and below[2474617]. |  
 
	| enum {
    gestaltNativeTimeMgr = 4  /* native Time Manager is present */
}; |  
	| Listing 7. "Gestalt.h" additions
for the native Time Manager. |  Developer ConsequencesThe native Time Manager publishes
         exactly the same programming interface as the extended Time
         Manager, so the developer consequences of the new
         implementation should be minimal. The following sections
         describe the gotchas we have seen so far. Testing for Time Manager
         FeaturesWhen testing whether a particular
         Time Manager feature is available, always compare the result
         of gestaltTimeMgrVersionwith the "greater than" operator rather
         than the "equals" operator. For example, the code in Listing
         8 tests whether the features of the extended Time Manager
         are available. If you test for equality, your code will do
         the wrong thing when the native Time Manager is
         available. 
	| static Boolean HaveExtendedTimeManager(void)
{
    UInt32 response;
 
    return (Gestalt(gestaltTimeMgrVersion,
                    (SInt32 *) &response) == noErr)
            && (response >= gestaltExtendedTimeMgr);
} |  
	| Listing 8. Detecting the extended Time Manager. |  Error ResultsThe native Time Manager can return
         an error in cases where previous versions of the Time
         Manager never did. It is important that you check and handle
         error results from calls to the Time Manager. See
         Time Manager
         Errors for details.
          Back to top 
 Time Manager ErrorsSince its initial release, the Time
         Manager's routines have always been defined to return an
         error result in register D0 (see Inside Macintosh IV, page 300). However, the high-level glue
         for calling these routines has never exposed these error
         results. This is particularly problematic for the native
         Time Manager, which can return an error in cases where
         previous versions of the Time Manager never did. Apple has addressed this problem by
         defining new high-level interfaces for the Time Manager. The
         prototypes for the new routines are shown below. 
	| extern pascal OSErr InstallTimeTask (QElemPtr tmTaskPtr);
extern pascal OSErr InstallXTimeTask(QElemPtr tmTaskPtr);
extern pascal OSErr PrimeTimeTask   (QElemPtr tmTaskPtr, long count);
extern pascal OSErr RemoveTimeTask  (QElemPtr tmTaskPtr); |  
	| Listing 9. New Time Manager high-level glue. |  These routines are identical to the
         original Time Manager routines except that they return an
         error result. The routines were first exported by in
         Universal Interfaces 3.3, however they were only callable
         from 68K code. Calling these routines from PowerPC code
         required two extra steps. 
            Mac
            OS 9.0.4 exports these
            routines from InterfaceLib.A future version (post-UI
            3.3.2) of Universal Interfaces will include an
            InterfaceLib stub library that includes these
            routines. You can simulate these routines for
         PowerPC code running on earlier systems by writing your own
         CFM glue. See DTS Technote 1127 In
         Search of Missing Links for
         the gory details. However, an easier solution is to use the
         glue provided by the MoreInterfaceLib module of the DTS
         sample code library MoreIsBetter. Back to top 
 SummaryThe following points explain what you should and should
         not do in working with the Time Manager. 
            Always follow the basic
            rules.Always set
            tmReservedto 0
            before callingInsXTime.Set tmWakeUpto
            0 before the first call toInsXTime, never
            look at it or modify it (except to set it to 0 in some
            cases, no other value is acceptable) afterwards.tmCountis only valid after a call toRmvTime.Always clear qLinkbefore callingInsXTime.Microsecondsis good alternate way to measure elapsed time.Always check Time Manager error
            results. 
 ReferencesInside
         Macintosh: Processes, Chapter 3, The Time Manager
          Denis
         G. Pelli's Web page DTS Technote 1127 In
         Search of Missing Links DTS Technote 1194 Mac
         OS 9.0.4 Back to top 
 Change History
 Back to top 
 Downloadables
            
               | 
 | Acrobat version of this Note (64K). | Download |  Back to top |