CPGuide - Timers

From EDM2
Jump to: navigation, search

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Control Program Programming Guide and Reference
  1. Introduction to the Control Program
  2. Control Program Functions
  3. Keyboard Functions
  4. Mouse Functions
  5. Video Functions
  6. Data Types
  7. Errors
  8. Debugging
  9. Kernel Debugger Communications Protocol
  10. Device I/O
  11. Dynamic Linking
  12. Error Management
  13. Exception Management
  14. Extended Attributes
  15. File Management
  16. File Names
  17. File Systems
  18. Generic IOCtl Commands
  19. Memory Management
  20. Message Management
  21. National Language Support
  22. Pipes
  23. Program Execution Control
  24. Queues
  25. Semaphores
  26. Timers
  27. Notices
  28. Glossary

This chapter describes how to create and use timers. Timers enable applications to time events by waiting for an interval to elapse or by waiting for a semaphore to be posted.

The following topics are related to the information in this chapter:

  • Program execution and control
  • Semaphores

About Timers

Because OS/2 is a multitasking system, an application cannot predict when it will lose execution control or how much time will elapse before control returns. A timer enables an application to suspend operation for a specific length of time, to block a thread until an interval has elapsed, or to post an event semaphore at repeated intervals.

Timers are managed by OS/2. When an application requests a timer, the system monitors the system clock and notifies the application when the interval has elapsed.

The system clock counts the number of system-clock interrupts (clock ticks) that have occurred since the system was started. On most hardware, clock ticks occur approximately 32 times a second, so the length of a tick is approximately 31.25 milliseconds.

When an application specifies a timer interval, the system rounds up the interval to the next clock tick. For example, if an application requests a 10 millisecond interval, it will sleep for at least 31.25 milliseconds. If an application requests a 100 millisecond interval, the actual interval will be at least 125 milliseconds (4 ticks).

Because OS/2 is a preemptive operating system, there is no guarantee that a thread will resume immediately after the timer interval. If a higher priority process or thread is executing, the timed thread must wait.

Although timers are not absolutely accurate, they can be used where the inaccuracy can be ignored. In a real-time control application, for example, an event can be timed in seconds or minutes, so an error of a few milliseconds is unimportant. If an application requires as much accuracy as the system can provide, it can dedicate a thread to managing timer intervals and then elevate the priority of that thread.

Suspending Threads

An application can use DosSleep to suspend operation of a thread for a specified interval. The system waits the specified number of milliseconds (subject to the round-off error just discussed) before returning control to the application. Because a sleeping application yields execution control to the system, the system can execute other processes or threads while the application sleeps.

The following code fragment shows how to suspend the calling thread for one minute:

 #define INCL_DOSPROCESS   /* Process and thread values */
 #include <os2.h>
 #include <stdio.h>
 
 ULONG   ulTimeInterval;   /* Interval in milliseconds  */
 APIRET  ulrc;             /* Return code               */
 
 ulTimeInterval = 60000;
 
 ulrc = DosSleep(ulTimeInterval);
 
 if (ulrc != 0) {
     printf("DosSleep error: return code = %ld",
            ulrc);
     return;
 }

See #Suspending the Current Thread for more information on DosSleep.

Asynchronous Timers

DosSleep is useful for temporarily suspending a thread but is much less useful for timing. Typically, an application carries out part of its task and then waits an interval. If the execution time varies (as it will if the application runs on different hardware), the overall interval varies. In these situations, asynchronous timers provide greater precision than DosSleep.

OS/2 supports two types of asynchronous timers, single-interval (one-shot) and repeated. DosAsyncTimer starts a single-interval timer. During the timing interval, the application can carry out other tasks. The system posts an event semaphore when the timing interval elapses. The application can reset the semaphore with DosResetEventSem before starting the timer. DosAsyncTimer yields a more accurate timing interval than DosSleep because the interval is independent of the execution time.

DosStartTimer starts a repeated timer. The system posts an event semaphore each time the interval expires. The application can reset the semaphore before starting the timer and after each posting. When the application resets the semaphore with DosResetEventSem, it can check the cPosts value to determine how many times the semaphore has been posted. If the semaphore has been posted more than once, the application has missed a timer interval.

Using Timers

Applications frequently need to synchronize the execution of threads, to cause an event to occur after a specified interval, or to cause an event to occur at regular intervals. Timers are typically used to enable an application to pause before processing user input or to carry out a task at a given time.

OS/2 provides the following timer functions:

  • DosSleep suspends the execution of the calling thread, enabling other threads to run while the calling thread sleeps.
  • DosAsyncTimer starts a single-interval timer.
  • DosStartTimer starts a repeated-interval timer.
  • DosStopTimer stops a single-interval or repeated-interval timer.

The system also provides two functions, DosGetDateTime and DosSetDateTime, for getting and setting the system date and time.

The timers that are started by DosAsyncTimer and DosStartTimer are asynchronous timers; that is, the timers run independently of the calling thread, enabling the calling thread to perform other operations while the timer is running. When an asynchronous timer interval expires, the system notifies the application by posting an event semaphore.

Time intervals for DosAsyncTimer, DosStartTimer, and DosSleep are specified in milliseconds; however, it is important to recognize that the actual duration of the specified time interval will be affected by two factors:

  • First, the system clock keeps track of time in less precise units known as clock ticks. On most hardware, clock ticks occur approximately 32 times a second, so each tick interval lasts approximately 31.25 milliseconds. (To determine the duration of a clock tick on your computer, call DosQuerySysInfo, and examine the timer-interval field.)
    Because clock ticks are less precise than millisecond values, any time interval that is specified in milliseconds will essentially be rounded up to the next clock tick.
  • Second, because OS/2 is a priority-based, multitasking operating system, there is no guarantee that a thread will resume execution immediately after the timer interval expires. If a higher priority process or thread is running, or if a hardware interrupt occurs, the timed thread blocks. (To minimize delays caused by preemptive scheduling, an application can dedicate a thread to managing time-critical tasks, and then raise that thread to a higher priority.)

These factors usually cause the timer interval to be longer than requested; however, it will generally be within a few clock ticks.

Timers for Presentation Manager applications are provided through the message queue. Therefore, a Presentation Manager application will not use the timer functions unless it performs some real-time control task.

Note
In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.

Suspending the Current Thread

An application can suspend a thread by using DosSleep. DosSleep suspends the execution of the calling thread for a specified time interval.

DosSleep requires one argument-the amount of time (in milliseconds) to suspend the thread. This value is rounded up to the nearest clock tick. If a time interval of 0 is specified, the thread gives up the remainder of the current time slice, enabling other ready threads of equal or higher priority to run; the calling thread will run again during its next scheduled time slice. If there is no other ready thread of equal or higher priority, DosSleep returns immediately; it does not yield to a thread of lower priority.

If there is a round-off error or if other threads in the system have higher priority, a thread might not resume execution immediately after the sleep interval.

The following DosSleep call suspends a thread for at least 5 seconds:

DosSleep(5000);

Note that the specified time interval refers to execution time (accumulated scheduled time slices), not to elapsed real time. Elapsed real time will be longer and will vary, depending on the hardware and on the number and priorities of other threads running in the system. In addition, even though the calling thread is scheduled for execution as soon as the specified time interval has elapsed, its execution could be delayed if a higher priority thread is running or if a hardware interrupt occurs.

Because the above factors usually cause the sleep interval to be longer than requested (though generally within a few clock ticks), DosSleep should not be used as a substitute for a real-time clock.

Note
  1. Elapsed real time for the asynchronous timers (started by DosAsyncTimer and DosStartTimer) will be much closer to their specified time intervals because these timers run independent of the execution of the calling thread.
  2. To ensure optimal performance, do not use DosSleep in a single-thread Presentation Manager application. (Use WinStartTimer.)

Timing a Single Interval

To carry out other tasks while the timer counts an interval, an application can use DosAsyncTimer. This function sets a single-interval timer without stopping the application-the timer runs asynchronously to the calling thread, enabling the thread to perform other operations while it is waiting for the specified time interval to expire. When the interval elapses, OS/2 notifies the application of the expiration of the timer by posting an event semaphore. The application resets the semaphore before starting the timer and monitors the semaphore to determine when the time has elapsed. The application can use DosCreateEventSem with the initial state FALSE to create a reset semaphore. For more information on semaphores, see #Semaphores.

The following code fragment creates an event semaphore and then calls DosAsyncTimer to count an interval while the application performs other tasks:

 #define INCL_DOSDATETIME    /* Date/Time and Timer Support */
 #define INCL_DOSSEMAPHORES  /* for semaphores              */
 
 #include <os2.h>
 
 HEV hev;
 HTIMER hTimer;
 
 /* First create a private, reset, event semaphore. */
 
 DosCreateEventSem((PSZ) NULL,
                   &hev,
                   DC_SEM_SHARED,
                   FALSE);
 
 /* Start async (one-shot) timer; post semaphore in 10 seconds.  */
 
 DosAsyncTimer(10000,       /* Time in milliseconds (10 sec)     */
               (HSEM) hev,  /* Semaphore handle                  */
               &hTimer);    /* Timer handle (used to stop timer) */
 
     .
     .    /* Do other processing here, then wait for semaphore.  */
     .
 DosWaitEventSem(hev,
                 SEM_INDEFINITE_WAIT);

Before starting the timer, the thread creates the event semaphore with DosCreateEventSem, specifying its initial state as reset. If the semaphore was previously created by the same process, the thread resets it by calling DosResetEventSem. If the semaphore was previously created by another process, then the thread must call DosOpenEventSem to gain access to the semaphore before calling DosResetEventSem.

Next, the thread calls DosAsyncTimer, specifying the handle of the event semaphore and the desired time interval. The thread can then perform other tasks. However, in order for the application to be notified of the expiration of the timer, one or more threads in the application must call DosWaitEventSem.

When the time interval expires, the system posts the semaphore, and any threads that were blocked on DosWaitEventSem requests can resume their execution. If another time interval is required, the semaphore is reset, and both DosAsyncTimer and DosWaitEventSem are called again. (To time regular repeated intervals, use DosStartTimer.)

The timer can be canceled before its time interval expires by calling DosStopTimer.

Timing Repeated Intervals

To count an interval repeatedly, an application can use DosStartTimer. This function starts a repeated-interval timer.

Unlike DosAsyncTimer, DosStartTimer does not stop after the first interval is counted. The timer runs asynchronously to the calling thread, enabling the thread to perform other operations while it is waiting for the specified time intervals to expire. The system notifies the application of the timer's expirations by posting an event semaphore.

The application resets the semaphore before starting the timer and whenever the system posts the semaphore. The application can use the value returned in the post-counting parameter by DosResetEventSem to assure the semaphore was posted only once before it was reset.

The following code fragment starts a timer and then waits on and resets the associated event semaphore. Assume that the handle of the targeted event semaphore has been placed into SemHandle.

 #define INCL_DOSDATETIME   /* Date and time values */
 #include <os2.h>
 #include <stdio.h>
 
 ULONG    ulTimeInterval;   /* Interval (milliseconds) */
 HSEM     hsemSemHandle;    /* Event-semaphore handle  */
 HTIMER   hHandle;          /* Timer handle (returned) */
 APIRET   ulrc;             /* Return code             */
 
 ulTimeInterval = 30000;    /* Set the periodic time interval to */
                            /* elapse every 30 seconds           */
 
 ulrc = DosStartTimer(ulTimeInterval,
                      hsemSemHandle,
                      &hHandle);
 
 if (ulrc != 0) {
     printf("DosStartTimer error: return code = %ld",
            ulrc);
     return;
 }

On successful return, Handle will contain the handle of this periodic timer. DosStopTimer can be used later to stop the periodic timer.

A repeated timer will continue to count the interval and post the semaphore until the application terminates or the application uses DosStopTimer to stop the timer explicitly. The following code fragment shows how to stop a periodic timer that has been started previously with DosStartTimer:

 #define INCL_DOSDATETIME   /* Date and time values */
 #include <os2.h>
 #include <stdio.h>
 
 HTIMER   hHandle;     /* Handle of the timer */
 APIRET   ulrc;        /* Return code         */
 
 ulrc = DosStopTimer(hHandle);
 
 if (ulrc != 0) {
     printf("DosStopTimer error: return code = %ld",
            ulrc);
     return;
 }

Before starting the timer, the event semaphore must be reset. If the semaphore does not exist, the thread can create it with DosCreateEventSem, specifying its initial state as reset. If the semaphore was previously created by the same process, the thread resets it by calling DosResetEventSem. If the semaphore was previously created by another process, then the thread calls DosOpenEventSem to gain access to the semaphore before calling DosResetEventSem.

Next, the thread calls DosStartTimer, specifying the handle of the event semaphore and the desired time interval. The thread can then perform other tasks. However, in order for the application to be notified of the timer's expirations, one or more threads in the application must call DosWaitEventSem.

When the time interval expires, the system posts the semaphore, and any threads that were blocked on DosWaitEventSem requests can resume their execution. Each time the semaphore is posted, it must be reset with DosResetEventSem before the next timer expiration. DosWaitEventSem must also be called to wait for the semaphore to be posted again.

In addition to resetting the event semaphore, DosResetEventSem returns the semaphore's post count (the number of times the semaphore has been posted since the last time it was in the set state). An application can use the post count to ensure that it has not missed a timer interval; if the post count is greater than one, the application missed a timer interval.