Multi-Threaded Applications
Multi-threaded applications can get quite complex and, as such,
need lots of care in their design, implementation and testing. Fortunately,
with regard to power management efficiency, they are not much more complex than
single-threaded applications.
First, it is important to notethat the previous discussion about
single-threaded applications and power management is also relevant to multi-threaded
applications. Everything that was mentioned in the single-threaded application
section applies here. The only real difference is that there are more considerations
and possibilities with multi-threaded code. Each thread in the process, must somehow
interact with the other threads. Normally, this can be done by posting messages to a
thread, or by using objects such as semaphores, events, mutexes and critical
sections. Usually these objects are used for synchronizing code, allowing one thread
to control when another thread executes.
Like a single-threaded application, a multi-threaded application needs
to consider when the entire process (all threads in a multi-threaded application) is
in a "wait" state in order for Windows 95 power management activity to
occur. For example, if all threads are in a WaitMessage waiting for a message to be
posted to its message queue, then power management activity will occur. However, if
just one of the threads is executing a PeekMessage loop, no power management
activity will be performed.
The APIs available to Windows 95 applications for waiting for objects
can have the same characteristics as the GetMessage API, if passed with the
correct arguments. Each of these "wait for object" APIs has a parameter
designating a timeout in milliseconds. This timeout parameter can be zero, in which
case the object is tested and the call returns immediately. If the timeout value is
INFINITE, only the object becoming signaled can force the call to return. Other values
indicate the call will timeout if the object isn't signaled before a specified time
elapses. The list of "wait for object" APIs are:
- MsgWaitForMultipleObjects
- WaitForMultipleObjects
- WaitForMultipleObjectsEx
- WaitForSingleObject
- WaitForSingleObjectEx
Like GetMessage, all these APIs will allow Windows 95 to idle
normally while the API is waiting for an object to become signaled, that is, when
the timeout value passed is not zero. If the timeout is zero then the function
returns immediately without allowing power management activity. Therefore, use a
timeout value of zero only when necessary. Wherever possible try to use an INFINITE
timeout value, otherwise, even small values allow the system to idle
Drivers
Like applications, drivers in Windows 95 must also be written
carefully to make sure they don't prevent power management from occurring in the
system. There are two types of drivers in Windows 95: ring 3-drivers (.DRVs) and
ring-0 drivers (.VxDs). DRVs are a kind of glorified DLLs, so the same rules apply
to them as to applications.
VxDs, on the other hand, have a completely different set of APIs
available to them. At least a few of these APIs have been proven to prevent the power
management mechanism in Windows 95. Among these are the BlockOnIdle and
Wait_Semaphore APIs.
Many Windows*-based VxDs use the VMM call pairs _BlockOnID / _SignalID
and Wait_Semaphore / Signal_ Semaphore for thread synchronization. Generally, the
signaling of a thread occurs soon after it blocks. However in some cases, the time between
a thread blocking and its signaling can be very long, sometimes lasting minutes
or hours.
While the Wait_Semaphore call does include a Block_Thread_Idle flag
that tells the Windows* system to "consider the thread idle when it blocks on
the semaphore," the _BlockOnID call does not have such a flag. Therefore,
threads that use the _BlockOnID call are considered non-idle when it blocks on
the ID.
A VxD should not call _BlockOnID or Wait_Semphore without the
Block_Thread_Idle flag set unless there is a compelling reason for not allowing the
Windows* system to go idle. Using these calls unnecessarily causes system performance
problems. The Windows* scheduler checks to see if there are any outstanding non-idle
blocking threads before it allows background driver processing to occur, consuming
CPU time and taking time away from other processes.
Many power management methods used on laptop and notebook computers
are based on the
system going idle when there is no processing to do. A VxD using the
_BlockOnID or Wait_Semaphore
(with the Block_Thread_Idle flag
cleared) calls unwisely will make the system appear busy to power management software,
resulting in excessive power consumption and shortening the time that the user can
run the system on battery power.
In the future, the Windows* system will make more and more use of
idle time to do background processing, which is designed to optimize system
performance. VxDs that do not allow the system to go idle will adversely affect
the performance of these techniques.
All these problems can be avoided by calling _BlockOnID only when
the thread will not need to block for an extended period of time or by using the
Wait_Semaphore with the Block_Thread_Idle flag set.
VxDs also have the option of registering for idle notification by
calling the Call_When_Idle API, to which they pass a callback address. This allows
the VxD to perform processing whenever the system goes into an idle state. When the
VxD's idle callback handler is called, the normally returns from the handler with
the carry flag set. This tells Windows 95 that it is okay to enter the idle state.
However, if the VxD returns from the idle callback handler with the carry flag clear,
this forces Windows 95 to stop the idle notification callbacks and to skip any power
management activity. Returning with the carry flag clear from an VxD idle callback
handler must be done with great care to ensure that power management is performed
whenever possible. It would not be acceptable to write a VxD such that it
always prevents power management from occurring in the system.
Optimizing the System
Besides software, a known setting in the system can effect how efficient
power management works in certain situations. The KeyIdleDelay setting in the [386Enh]
section of SYSTEM.INI controls the non-idle time after a keystroke while in a DOS box.
By default, this value is set so that after pressing a key, the system will remain
non-idle for about 1/2 of a second. This causes the system to remain non-idle while
typing DOS commands or editing a document within a DOS box.
Setting the KeyIdleDelay equal to 0.005 can help the system to idle
normally while typing in a DOS box and save power. However, don't use zero for this
setting, because this may cause a symptom where the ALT key is not recognized at all
times.