Wednesday, 30 December 2009

Writing a correct Main Loop for your application

Have you ever wondered how can Windows run more than one application at a time, even in a single-processor machine? The answer is easy. It doesn’t. The point is that it switches from one application to another so fast that you don´t notice the change, and it seems both applications are running in parallel.
To allow this, running Windows applications should not always be running.
It´d be a waste of resources to massively use the processor just to find out that nothing had to be done, don’t you think?. That´d be kind of a psychotic behavior. Windows has it´s protocols and procedures to avoid this, letting applications or threads enter idle states, even for a few milliseconds, and giving then priority to other, non-idling processes.
So, what would happen if your application never enters idle mode? It would bring down the multi-tasking capability of Windows, slowing down every other application than yours. That´s why I say that running applications should not always be running.
Under this point of view, applications can be divided into three groups:
  1. Psychotic applications that should be updated always, i.e. intense computing applications (not so frequent, nowadays)
  2. Applications that should be updated only as a response to some user interaction (most Windows Forms applications)
  3. Applications that sometimes need to be “always updated”, and sometimes not (like a video-game)
As you know, when an application is run, the Main method is invoked, and the O.S gives the application access to the processor and some memory. Apart from thread priority policies, application´s behavior will be one or another depending on what we do in this Main() method:

Case # 1. Compute, compute, compute

The Main method structure is simple: update as fast as you can, and consume as much processor as possible. The Main() method in pseudo-code:
            while (Application is Alive)
            {
                Compute()
            }
This is the typical behavior of old applications in non multi-tasking environments. They consume as much processing power as is available.

Case # 2. Don´t disturb unless absolutely necessary

What should be done here is easy: if events are pending, process them (DoEvents). If not, just relax and sleep, yielding processor to other threads or applications. The exact implementation of such a procedure can be more complex, as we should decide when to enter sleep modes and when not. Luckily, .Net does that for us. The C# implementation you should use is:
            System.Windows.Forms.Application.Run(mForm);
The Run() method takes care of all the things above mentioned (yielding and many other things), and it does it using the standard .Net procedures and protocols.

Case # 3: The mixed approach

Other situations need a mixed solution. For example, a Windows Forms application that sometimes needs to enter a “running” state, where everything has to be updated, like a Windows Forms-based video-game, or a 3D editor where you want to run an animation. Here, updates will be applied when the application is in Running state, but not when it´s not, saving this way CPU (and batteries, if running in a laptop).
So, we want to combine the efficiency of Case # 2, with the possibility of switching to Case # 1 when needed. The correct implementation for this, in C#, is:
       ...
            System.Windows.Forms.Application.Idle += new EventHandler(Application_Idle);
            System.Windows.Forms.Application.Run(mForm);
        }
        static void Application_Idle(object sender, EventArgs e)
        {
            if(Application.RunningState)
            {
                 while(ApplicationStillIdle)
                      Compute();
            }
        }
The Application_Idle event will be fired each time the application is about to enter in Idle state (where all the pending messages for the windows has been processed).
This way, interaction between our application and others (running at the same time) will be correct: when we decide it (activate running state), we will have all the computing power for ourselves, but when it´s not needed (RunningState is off), we will yield it.

How to check if application is still Idle

The Application_Idle event is fired only once, when the application is about to enter idle state. Here, we will have to update computations while it is still Idle, giving priority to Windows Messages if there are some. How to check if the application is still Idle? Easy, we can use a property like the following:
        /// <summary>Checks to see if the application is still idle</summary>
        private bool AppStillIdle
        {
            get
            {
                NativeMethods.Message msg;
                return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
        }

An extra option to make things even better

We have seen how to activate/deactivate the psychotic (processor consuming) behavior of an application, basing on a state variable, but sometimes it is interesting to also deactivate it when our application does not have the focus (it is not the active application in the O.S.).
Windows controls and forms have a “Focused” property, but that doesn’t help much in this case, as we want to check if the entire application has focus, not an specific form (the main form of the application will loose focus often, when we open a child window, for instance).
Then, how to check if our application is the active application in Windows? Unfortunately, it seems that there is no C# managed code to check that, but we can easily use DLLImport to invoke the “GetForegroundWindow” method, comparing its result to our main form handle:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
 
public bool MyAppFocused
{
    get { return GetForegroundWindow() == this.Handle; }
}
This way, we can put an additional condition to the Compute method, deactivating it also when the application is not focused:
static void Application_Idle(object sender, EventArgs e)
{
   if(Application.RunningState && MyAppFocused)
   {
       while(ApplicationStillIdle)
            Compute();
   }
     }

What should not be done

Some people tend to write the following code when need the mixed solution mentioned:
while (mForm.Created)
{
      System.Windows.Forms.Application.DoEvents();
 
      if(Application.RunningState)
             Compute();
}

Though it works, this approach is not correct, as it doesn’t yield processor to other threads (your application won´t enter idle mode). Use the above Idle event approach instead.

Conclusion

Quality software use the processor when really need it. Adding a few extra lines of code, you software will better share resources with other applications, and will improve laptop or handheld device´s battery life.
Take Care!

No comments: