Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way for a program to terminate its own process (Windows)

C# .NET 3.5

I have a console application that is being called by another application on the computer. This console app runs continuously, and listens for data on stdin from the "parent" process.

However, when the parent is stopped or killed, the console app that it started continues. Under normal circumstances, it sits and idles waiting for input from stdin, using minimal resources. However, as soon as the parent goes away, this console app spikes the CPU and starves the core it's running on with near 100% utilization. This continues until I manually kill the process.

Ideally, the calling parent would clean up after itself, particularly since this is happening under normal (non exceptional) "stop" conditions. Unfortunately, this parent process is out of my hands.

My first thought was to grab the invoking parent from within the console app, and monitor its PID. If the parent process goes away, I the console app would terminate itself. Currently, I'm doing this by:

Process process = Process.GetCurrentProcess();
m_ParentPID = 0;
using (ManagementObject mgmtObj = new ManagementObject("win32_process.handle='" +     process.Id.ToString() + "'"))
{
    mgmtObj.Get();
    m_ParentPID = Convert.ToInt32(mgmtObj["ParentProcessId"]);
}
string parentProcessName = Process.GetProcessById(m_ParentPID).ProcessName;
Log("Parent Process: " + parentProcessName + Environment.NewLine);

// Create a timer for monitoring self.
Timer timer = new Timer(new TimerCallback(sender =>
{
    if (m_ParentPID != 0)
    {
        Process parent = System.Diagnostics.Process.GetProcessById(m_ParentPID);
        if (parent == null)
        {
            Log("Parent process stopped/killed.  Terminating self.");
            System.Environment.Exit(0);
        }
    }
}));

// Kick on the timer
timer.Change(m_ExitWatcherFrequency, m_ExitWatcherFrequency);

This only partly works though - it stops the CPU spike, but if I look at my processes from Sysinternals wonderful process monitor, I can see DW20.exe running - the "Microsoft Application Error Reporting" program. And it just ... sits there, and the console app remains in memory.

What should I be doing here to correctly terminate the process to avoid this continuous CPU spike and unreleased memory? Eventually this needs to be running without intervention.

P.S. I am using a command line application here as a "long running program" instead of a Windows Service or web service because the parent program can only be configured to execute a command line app for which it passes data in via stdin. (for those who are curious, this is ejabberd, using external authentication).

EDIT:

The code that waits for input from stdin is:

// Read data from stdin
char[] charray = new char[maxbuflen];
read = Console.In.Read(charray, 0, 2);

I mention before that when the parent terminates, the console app goes crazy on the CPU. I attached a debugger to it from Visual Studio, and it is, in fact, still sitting on that line Console.In.Read. In theory then, when the self-monitoring timer triggers and sees the parent is gone, it attempts an System.Environment.Exit(0) when the other thread is on that Read() line.

like image 327
Matt Avatar asked Dec 29 '25 22:12

Matt


2 Answers

It sounds like your process is going into a hard loop due to the console input stream being closed when the parent exits. Are you checking the return value from Console.In.Read? It will return zero when the stream has been closed. At that point break out of the loop and let your Main() method exit by itself.

And if you're running multiple threads they'll have to be finished first, using Thread.Join or equivalent.

like image 70
devstuff Avatar answered Dec 31 '25 14:12

devstuff


To watch for a process exit, use the Process class, and subscribe to the Exited event.

Edit: Removed interop comment.

Edit (in response to comments):

Most times, what is done in a situation like this, is pass some data back in, so you can stop blocking on the Console.In.Read method call.

So, suppose you set a flag, IsDone, to true when you discover the parent process is done. Now, because the process you're waiting for wont send anything anymore, you still need to receive something on standard input, or you'll block forever. So, in your event handler/timer code, write something to the standard input of your own process (it can even be a special value to signal you're done if you want). This will get you past the Console.In.Read method. Once out, check to see if the IsDone flag is set -- if it is, stop processing and return from your main method -- No System.Environment.Exit required.

like image 38
Nader Shirazie Avatar answered Dec 31 '25 14:12

Nader Shirazie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!