Windows CE has supported 256 thread priorities since version 3.0. Microsoft increased the number of thread priorities at that time to enhance the Windows CE real-time performance. With so many thread priorities, how can a system engineer determine which threads to set at which priority? I am going to attempt to give some guidance for setting the thread priorities in the article.
The thread priorities currently supported are 0 through 255. Zero is the highest priority and 255 is the lowest. What this means that if a thread needs to execute and it is at a higher priority than other threads in the system, it will run. If there are other threads at the same priority, they will be run one after the other in a round robin manner.   Once the high priority threads have completed needing to execute and block, the next lower priority thread that needs to execute will be started.
This means that high priority threads will prevent lower priority threads from running. So all threads need to be written so that they do not use excessive CPU time or the overall system may suffer.
There is an interesting twist on this priority scheme known as priority inversion. Priority inversion is an OS feature that is in place to prevent deadlock. When two or more threads share a resource, like a Mutex or Critical Section, if the higher priority thread is blocked waiting for access to the resource the lower priority thread will have its priority raised up to the priority of the higher priority thread so that the lower priority thread can execute to release the resource. As soon as the lower priority thread releases the resource, its priority will be lowered back to its original priority.
It is important to understand the functions used for setting the thread priorities. There are two functions for setting the priority; CeSetThreadPriority() and SetThreadPriority().  
CeSetThreadPriority()
CeSetThreadPriority() was introduced with Windows CE 3.0 to handle the increased thread priorities. CeSetThreadPriority() takes two parameters; the thread ID and a number between 0 and 255.
SetThreadPriority()
SetThreadPriority() is the original function for setting the priority in the early versions of Windows CE. SetThreadPriority() is maintained today for backward compatibility. SetThreadPriority() is capable of setting the priority to one of the original eight priorities, but these priorities map into the lowest eight priorities since Windows CE 3.0. SetThreadPriority() takes two parameters; the first is the thread ID and the second is one of eight macros. The macros are:
                THREAD_PRIOTIY_TIME_CRITICAL
                THREAD_PRIORITY_HIGHEST
                THREAD_PRIORITY_ABOVE_NORMAL
                THREAD_PRIORITY_NORMAL
                THREAD_PRIORITY_BELOW_NORMAL
                THREAD_PRIORITY_LOWEST
                THREAD_PRIORITY_ABOVE_IDLE
                THREAD_PRIORITY_IDLE
These macros should never be used with CeSetThreadPriority(). They were not intended for this purpose.
In general, I don’t recommend using SetThreadPriority() because I have seen too many application programmers use it incorrectly; they think that THREAD_PRIORITY_TIME_CRITICAL sets the application thread to a very high priority. Let me tell you now that it does not. Instead, it sets the thread priority to a high priority relative to other functions using SetThreadPriority(), but in fact this priority is 248 on most Windows CE devices.
SetThreadPriority() can be a good function to use within application because it restricts the priorities that an application uses. But if used in an application, the programmer needs to understand what the function actually does.
Now that we have some functions for setting the priority, we can look at some guidelines for choosing the priority. Microsoft has documented some suggestions which are:
Priority Level
Suggested Use
0-96
Real-time threads
97-152
Default drivers
153-247
Non-real time drivers
248-255
Applications
 
I changed the wording to try to clarify the priority levels that Microsoft recommended but the numbers are consistent. Of course these suggestions should be followed for any general purpose device, like a PDA or Terminal which might have third-party applications installed. But, many Windows CE devices are truly embedded systems that are packaged and tuned by an OEM for a special purpose. Don’t misunderstand me, these guidelines are good and in most cases should be followed, but there are times when the system engineer might need to tune the system by not following these guidelines.
Choosing the priorities for a system can be time consuming. To do it well, the system engineer needs to understand what threads are in the system and what their purpose is. Once the system engineer understands the threads in the system, then it is much easier to determine the priorities that are needed.
Basically, the system engineer needs to determine which threads are more important than others. But it is important to also understand what how CPU intensive the threads are, a high priority thread that uses too much CPU time can starve the low priority threads keeping them from running. In some cases, that is desirable, but in most cases it is not. One solution to this is to add blocking calls to the high priority thread to allow lower priority thread time to run, Sleep() and WaitForSingleObject() can be used for this.
The system engineer will set the priorities so that the important threads get CPU time when they need it. If two threads could need to get CPU time at the same time, they should be set to the same priority. The trouble is that a Windows CE system can have many threads and many of them need to get CPU time, but determining which threads are more important will help in making decisions.
One mistake that I have seen is changing the priority of a thread to a high priority because the thread is important, but with total disregard for the importance of other threads in the system. Sure that thread gets the CPU when it needs it, but possibly at the expense of keeping other important threads from running. I have worked with engineers who have done this and accomplished what they wanted which was to make sure that the important thread ran, but then found that the rest of the system suffered. So change the priorities with caution, but do change them when necessary.
For an example, let’s look at a system with three threads. Not very realistic, but I think that it will work well for discussion. The threads are a GUI thread, a data collection thread and a touch driver interrupt thread.
1.       The GUI thread needs to update data and be responsive to the user. Users are important and their impression of the system is important to selling product, so when the user wants to interact with the system we need to be responsive. The priority of the GUI thread can actually be low if the other threads in the system give it time to run once in a while.
2.       The product was designed to collect data. Missing data will be perceived as worse than not responding to the user. This thread priority needs to be high, but because data collection can be CPU intensive this thread needs share the CPU.
3.       The user interacts with the system through the touch panel. If we don’t detect when the user touches the panel, the GUI will certainly not respond. This thread priority also needs to be high, but processing touch panel input is not very CPU intensive. Setting this priority higher than the data collection thread will probably not cause data to be missed.
Confusing? Yes, but with some knowledge and a little experimenting setting priority can be accomplished. Microsoft also provides some tools that are valuable for evaluating the system including the Remote Kernel Tracker.
Copyright © 2009 – Bruce Eitman
All Rights Reserved