Timing out a request to start a process... [Fernando Vicaria]

Timing out a request to start a process... [Fernando Vicaria]

Rate This
  • Comments 3

This is another post to quickly cover an question that I saw recently on one of our usergroups...

How do we create a process and "wait" for it begin running for a finite amount of time and if the process is not up and running until then timeout the request? I used the word wait in quotes because what the user really meant was that he makes the request in a separate thread while the application's main thread was still going on with its business. Of course there are at least half a dozen ways to do that but I decide to use the System.Threading.Timer class for this example.

The code below ilustrates this example:

using System;
using System.Diagnostics;
using System.Threading;

/// <summary>
/// Time-out process if it's not started or responding
/// </summary>

public class TimeOutProc
{
    static int TIME_OUT = 2000// in milliseconds

    static public int Main()
    {
        // Comment out one of the next two lines...
        //Process p = new Process();

        Process p = Process.Start("notepad");

        // Use Timeout.Infinite for the period to guarantee that
        // the timer will fire only once.

        Timer timer = new Timer(
            new TimerCallback(TimeOut), p, TIME_OUT, Timeout.Infinite);

        // This is just a trick so you can see the timer firing,
        // in a real-life app you should not need this.
        Console.WriteLine("Press any key to finish...");
        Console.WriteLine();
        Console.ReadLine();

        // Get rid of timer when you're done.
        timer.Dispose();
        return 100;
    }

    // This is the callback method passed to the timer.
    static void TimeOut(object state)
    {
        try
        {          
            // Check if process has started and has a pid.
            // If not this will throw a InvalidOperationException.

            if (((Process)state).Id >= 0)
            {
                Console.WriteLine("Process has started...");

                // Just for fun...
                Console.WriteLine("Process is working OK. Kill it now!");
                Thread.Sleep(2000);
                ((Process)state).Kill();

                // You could also check if process is not responding
                // instead...

                if (!((Process)state).Responding)
                {
                    Console.WriteLine("Process not responding...");
                    ((Process)state).Kill();
               
                    // Optional: Make sure you have no dead object here.
                    state = null;
                }
            }
        }
        catch (System.InvalidOperationException)
        {
            Console.WriteLine("Process not ready...");
            // Optional: Make sure you have no dead object here.
            state = null;
        }
    }
}

As you can see I have added a call to Console.ReadLine() so that we could see the callback function firing. Another point to note is that we only want it to fire once and to accomplish that we pass Timeout.Infinite as the period parameter. We can test if the process has started by checking if it has a pid, if it does not what you get is an InvalidOperationException so we have to cover for this case.

Even if the process has a pid it does not mean that it's in a healthy state, it could be not responding for example. In that case we can just kill the process if we think that it's not the expected behavior after the timeout.

  • I usually expect better than this from the BCL blog ... The entire premise of this post is a little strange -- basically it's waiting 2 seconds to see if a process it started has gotten far enough along the Windows initialization path to have an ID yet. Generally if you want to solve this problem you'll need to have the process you spawn fire an Event, and you need to wait on that event. "Started" is a very process specific conecpt. The process gets it's id before any domain specific initialization has happened yet -- and this is likely to be where the holdup is anyway. Unless your system has become so slow that Windows can't even launch a new process yet, in which case this solution is likely the least of your problems.

    In addition to this solution being a particularly poor one to the problem, the code has a lot of issues as well. From:

    // Comment out one of the next two lines...
    //Process p = new Process();
    Process p = Process.Start("notepad");

    Where uncommenting the first line leads to the code not working since you never tell it which process to start.

    From a stylistic point of view (and especially if this were to be called from a library of some sort), the timer should really be wrapped in a using block, or at the very least a try ... finally instead of being manually disposed, and not cleaned up in the exception path.

    Again style, but state should likely be cast to process only once (and FxCop will complain about this code), since you're doing regular casts here which are much more expensive than "as" style casts.

    Also, since we're checking to see if Process.Id throws, it should be the only code in the try ... catch block, since right now if anything throws InvalidOperationException we consider the process dead.

    Finally:
    // Optional: Make sure you have no dead object here.
    state = null;

    You should never set an object to null in order to help out the GC -- that reference was going away at the end of the method anyway. In fact just by doing this, you're actually potentially hurting the ability of the GC to collect this parameter! Microsoft code samples should not be demonstrating this common mistake.
  • PingBack from http://mydebtconsolidator.info/story.php?id=19654

  • PingBack from http://workfromhomecareer.info/story.php?id=18659

Page 1 of 1 (3 items)