Asynchronous timers provide IDL with non-blocking timers. They offer the following advantages over widget-based asynchronous timers:

  • Asynchronous timers work in headless environments, independent of widgets
  • Higher theoretical resolution (Unix: nanoseconds, Windows: microseconds)
  • Optional high-precision repeating timer

Asynchronous timers can fire and have their callback invoked in these situations:

  • While IDL is idle.
  • While IDL is processing statements in the interpreter.

Asynchronous timers will not fire in these situations:

  • While IDL is processing C code, including both built-in routines and DLMs.
  • While IDL is processing a call to wait.
  • While native dialogs are onscreen [e.g., DIALOG_PICKFILE(), DIALOG_MESSAGE(), etc.], however, they will fire when dialogs built from IDL widgets have popped up.
  • While system callbacks are executing. Some examples of callbacks are: timer callbacks, widget event handlers, object cleanup methods, etc. When a timer fires while a callback is executing, its callback will be invoked when the current one finishes.

  • While explicitly blocked by the timer.Block method.

Note: Asynchronous timers in IDL are actually implemented as a collection of static methods. Because of this, there are no "Creation" or "Syntax" sections in this document for creating timers.

Methods and Additional Information

Examples


Example 1 - Callback Specified

;--------------------------------------------------------------------
; This program demonstrates simple usage of IDL asynchronous timers.  A timer
; is created and it fires once.  User data is passed to the timer's callback.
; The callback prints the timer's identifier and user data.
 
PRO myTimerCallback, id, userData
  COMPILE_OPT IDL2
   
  PRINT, 'myTimerCallback( ', STRTRIM( id, 2 ), ', ', userData, ' )'
   
END
   
   
PRO async_timer_example_1
  COMPILE_OPT IDL2
   
  ; Create a timer that will fire in 3.14 seconds.  The name of the callback is
  ; required but the user data is optional.  The user data can be a string,
  ; structure, hash, etc.
   
  id = Timer.Set( 3.14, 'myTimerCallback', 'woof' )
 
END

Example 2 - Object Specified

;--------------------------------------------------------------------
; This program demonstrates usage of IDL asynchronous timers with objects.
; A main program creates an object and then a timer is set on that object.
; When the timer fires, the object's "handleTimerEvent" method is invoked.
;
; Run this example by saving it as "asynctimerexample2__define.pro".
;--------------------------------------------------------------------
; AsyncTimerExample2 class
;
; The class must implement a method with the name "handleTimerEvent". This
; callback method's signature is the same as with timer callback procedures.
 
PRO AsyncTimerExample2::handleTimerEvent, id, userData
  COMPILE_OPT IDL2
  PRINT, 'AsyncTimerTester::handleTimerEvent( ', STRTRIM( id, 2 ), ', ', userData, ' )'
END
 
PRO AsyncTimerExample2__define
  COMPILE_OPT IDL2
  !null = { AsyncTimerExample2, $
     INHERITS IDL_Object $
  }
END
 
;--------------------------------------------------------------------
; main program
 
o = AsyncTimerExample2()
id = Timer.Set( 3.14, o, 'w' + 'o' + 'o' + 'f' )
END

Example 3 - Blocked Timers

; This program demonstrates the use of timer.Block and timer.Unblock
; to create a region of code that will not be interrupted by timer
; callbacks.
;
; A pointer to some "critical data" is created.  Both the main
; routine and timer callback can modify this.  Calls to timer.Block
; and timer.Unblock prevent the timer from altering the value
; before the main routine is done with it.  As soon as the safe
; zone is exited, the timer is allowed to fire.
;
; Note that the "busyWait" routine gives the timer every chance to
; execute, because timers can interrupt PRO code execution.
 
PRO handleTimer, id, userData
COMPILE_OPT IDL2
 
PRINT, '*** Timer Fired ***'
*userData = 0
 
END
 
PRO busyWait, t
COMPILE_OPT IDL2
 
t0 = SYSTIME( /SECONDS )
WHILE ( ( SYSTIME( /SECONDS ) - t0 ) lt t ) DO BEGIN
ENDWHILE
 
END
 
 
PRO async_timer_example_3
COMPILE_OPT IDL2
 
criticalData = PRT_NEW( 1 )
!NULL = timer.Set( 1.0, 'handleTimer', criticalData )
PRINT, 'Data before safe zone: ', STRTRIM( *criticalData, 2 )
timer.Block
busyWait, 2.0
PRINT, 'Data in safe zone: ', STRTRIM( *criticalData, 2 )
timer.Unblock
PRINT, 'Data after safe zone: ', STRTRIM( *criticalData, 2 )
 
END

Timer::Block


The Timer::Block method allows you to block an asynchronous timer. If a timer's callback code could potentially interrupt and disrupt other code, the timer should be blocked. An example is when the timer's calleback code could access the same data as the code that it might interrupt. See Example 3.

Code that runs in between the calls to Block and Unblock will not be interrupted by any timer. IDL automatically blocks timer callbacks from interrupting system callbacks, including timer callbacks, widget callbacks, drag and drop callbacks, etc. Explicit blocking is unnecessary in these types of callbacks. Invocations of the Fire method will work regardless of blocking.

The Block and Unblock methods must always be paired. They can be nested. If they are not paired, then timer blocking will not work as expected. To help debug mismatches, the procedures have a COUNT keyword that returns the current nesting level after the call to block or unblock. The returned value typically ranges from 0 to MaxLong, where 0 means blocking will not occur and values greater than 0 indicate the nesting level. Negative values indicate that too many calls to Unblock have been made, and Block and Unblock have not been correctly paired.

Syntax


Timer.Block [, COUNT]

Arguments


None

Keywords


COUNT

Set this keyword to a named variable that will contain a long integer with the nesting level of the Block and Unblock methods.

Timer::Cancel


The Timer::Cancel method cancels a timer.

Note: If you are within the Timer callback when you call Timer.Cancel, your UserData variable will become undefined. To avoid this behavior you should make a copy of your UserData variable before calling the Cancel method.

Syntax


Result = Timer.Cancel( ID )

Result = Timer.Cancel( ALL=value )

Return Value


Returns a 0 or 1 to indicate the success or failure of the call. A return value of 1 indicates the call was successful, while a 0 indicates that the identifier (id) was invalid and the call was not successful.

Arguments


ID

A long integer that is the timer's identifier. This is the same value as returned by the "set" function.

Keywords


ALL

Use in place of ID to indicate whether all timers should be canceled. Valid values are Boolean (1 for yes or 0 for no).

Note: If you specify /ALL (or ALL = 1) in conjunction with the ID argument, the /ALL takes precedence and IDL ignores the ID.

Timer::Fire


You can launch a timer immediately, before it expires, by using the Timer.Fire method. If you originally supply user data, it is replaced with the new user data.

Note: If you use Timer::Fire, the timer will be "complete" and will not go off again.

Syntax


Result = Timer.Fire( ID [, UserData ] )

Return Value


Returns a 0 or 1 to indicate the success or failure of the call. A return value of 1 indicates the call was successful, while a 0 indicates that the identifier (ID) was invalid and the call was not successful.

Arguments


ID

The timer's identifier (a long integer). This is the same value as returned by the "set" function.

UserData

Optional data to supply to the callback when you invoke it. This data replaces any user data specified when the timer was created.

Keywords


None.

Timer::Set


The Timer::Set method allows you to create an asynchronous timer.

You have two options available to create a timer. The first requires the specification of a callback procedure. The second requires an object that must have a method named handleTimerEvent.

In both cases user data is optional. The returned value is the timer's identifier, which can be used to cancel the timer or fire the timer early.

Note: Once IDL invokes the timer, its identifier is no longer valid (unless the REPEAT keyword is set). Namely, the <ID> cannot be used to cancel or fire the timer.

Syntax


ID = Timer.Set( Time, Callback [, UserData] [, /REPEAT] )

ID = Timer.Set( Time, Object [, UserData] [, /REPEAT] )

Return Value


Returns the ID (identifier) of the timer. Use this ID with the Timer::Cancel and Timer::Fire methods.

Arguments


Time

The amount of time in seconds until the timer should activate.

Callback

A string value specifying the name of the procedure that IDL should invoke when the timer activates.

The callback must have the following signature:

PRO Name, ID [, UserData]

Value Type Description
Name   Name of the routine. When the timer is set using an object, this must be of the form <class>::handleTimerEvent.
ID Long

The timer's identifier. This is the same value as returned by the "set" function.

UserData Any The data supplied when the timer was set.
     

Object

Object on which to call a method named handleTimerEvent when the timer activates.

UserData

Data to be delivered to the callback. The delivered data will be a copy of the original. If user data is not supplied then the callback receives !NULL. UserData can be any valid IDL value. Examples of valid IDL values include: a constant, variable, expression, an array, etc.

Keywords


REPEAT

Set this keyword to create a repeating timer. By default, IDL will only fire a timer once. If REPEAT is set then IDL will fire the timer repeatedly until the timer is cancelled.

Timer::Unblock


The Timer::Block method allows you to unblock asynchronous timers that have been blocked.

Syntax


Timer.Unblock [, COUNT]

Arguments


None

Keywords


COUNT

Set this keyword to a named variable that will contain a long integer with the nesting level of the Block and Unblock methods.

Additional Information on Timers


Widget vs. Asynchronous Timers


The table below lays out the main differences between Widget-based and Asynchronous Timers:

Feature Widget Timers Asynchronous Timers
Interrupt PRO code No Yes
Interrupt C code No No
Interrupt WAIT No No
Interrupt callbacks No No
Modal bases Will not fire until modal child dismissed. Fires even when modal base is present.
Floating base Fires even when floating base is present. Fires even when floating base is present.
Native dialogs* Will not fire until dialog is dismissed. Will not fire until dialog is dismissed.
Blocking Xmanager No difference. No difference.
Runtime/VM mode    

* The set of native dialogs include DIALOG_MESSAGE, DIALOG_PICKFILE, DIALOG_PRINTERSETUP, and DIALOG_PRINTJOB.

Resetting Timers


You can reset timers using any of the following actions:

Action Effect
.RESET_SESSION Cancels all pending timers and resets the first timer identifier back to a value of 1.
.FULL_RESET_SESSION Cancels all pending timers and resets the first timer identifier back to a value of 1.
<CTRL><C> Cancels all pending timers.
"STOP" button in the IDL Workbench Cancels all pending timers.

Repeating Timers


You can create a repeating timer in two different ways:

Manually Trigger

In your callback routine, you can start another timer with the same time and callback. For example:

PRO myTimerCallback1, id, userData
  COMPILE_OPT IDL2
  . . . <do work> . . .
  ; Create a new timer that will fire again in 0.01 seconds.
  if (~done) then id = Timer.Set( 0.01, 'myTimerCallback1' )
END
 
PRO async_timer_example_repeat1
  COMPILE_OPT IDL2
  ; Create a timer that will fire in 0.01 seconds.
  id = Timer.Set( 0.01, 'myTimerCallback1' )
END

Advantage: This way is simple to understand; the timer is guaranteed to not fire again until the callback has finished running.

Disadvantage: This way has an irregular interval. Even if you put the Timer.Set at the beginning of the callback, you are still limited by the IDL interpreter. In the above example, you would never get 100 callbacks per second, no matter how fast your callback code.

Automatically Repeating

To create a repeating timer you can set the REPEAT keyword when creating the timer. For example:

PRO myTimerCallback2, id, userData
  COMPILE_OPT IDL2
  . . . <do work> . . .
  ; Keep firing our timer unless we hit some stop condition.
  if (done) then void = Timer.Cancel(id)
END
 
PRO async_timer_example_repeat2
  COMPILE_OPT IDL2
  ; Create a timer that will fire 100 times per second.
  id = Timer.Set( 0.01, 'myTimerCallback2', /REPEAT )
END

Advantage: This technique is better for code that requires a regular interval, such as playing back a movie at 30 frames per second. In the above example, IDL will call your callback 100 times per second, regardless of how long your callback takes to run (as long as it's less than 0.01 seconds).

Disadvantage: If your callback takes a long time to execute, then the callbacks will be queued up and delivered when IDL is ready. If the number of queued callbacks exceeds the maximum (see Notes below) then the excess callbacks will be discarded. You should ensure that the execution time of your callback code does not exceed your repeat interval.

Notes on Using Timers


  • Timers alone will not keep an IDL runtime or virtual machine running. In those run modes, IDL runs a single routine and then exits, regardless of pending asynchronous timers.

  • Each timer uses a thread. The number of timers is therefore limited by the number of threads that a process can have. The default is system dependent and can be configured for your operating system. Other threads (non-timer threads) are included in the total.
  • The number of queued timer callbacks is limited by system resources, but is usually around 512. If a timer fires and the queue is full, that timer callback will be discarded.

Version History


8.3

Introduced

8.4

Added Block and Unblock methods; IDL no longer automatically interrupts callbacks.

8.5.2

Added REPEAT keyword to Timer.Set

See Also


Static Methods