Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

I'd solve this by adding a thread to your console app that basically monitors the presence of the calling app in the process table. This is all off the top of my head, but here's some pseudocode:

using System.Thread;
using System.Diagnostics;

main(){
     Thread monitoringThread = new Thread(new ThreadStart(monitor));
     monitoringThread.Name = "Monitor";
     monitoringThread.Start();
}

and in the function monitor:

void monitor(){
     Process[] theCallers = Process.GetProcessesByName("CallingProcessName");
     theCallers[0].WaitForExit();
     Process.GetCurrentProcess().Kill();
}

Assuming you only have one calling process in the process list, as soon as that process ends, this one will as well. You can also put much nicer cleanup code for your own process, rather than having it just end abruptly.

I like mmr's idea of watching the parent process from the child. If you have a way to pass the parent's PID on the command line, that would be even better. From inside the parent process, you can get the PID:

System.Diagnostics.Process.GetCurrentProcess().Id
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top