I have a somewhat simple Client/Server solution running over C# remoting (System.Runtime.Remoting).
The MarshalByRef object, which is used for the actual communication, however troubles me.
In its constructor it starts up an external process and adds the OutputDataReceived eventhandler.
Every 2 seconds the process will write data to its stdout, which I then retrieve in my eventhandler and use there.
Now my problem is how to cleanup after this process - the thing is, it runs indefinitely so I have to call Kill() on it to stop it.
I tried doing this in the MarshalByRef object's finalizer, however this caused my application to hang every now and then, when trying to call the finalizer.
The remoting is setup as a Singleton, so it should be the server side which calls the finalizer, so frankly I find it a bit odd it doesn't work.
The class that spawns the process is actually a field of my MarshalByRef object, but that really shouldn't change a lot.
Also the class is run in mono on a linux server, and reads out CPU load from the tool mpstat.
Here's the class which troubles me:
internal class CpuInfo
{
private Regex parser = new Regex(@"\d{2}:\d{2}:\d{2}\s+(?<CpuID>\d{1,2}).*?(?<IdlePercentage>\d{1,2},\d{1,2})(\r|\n|$)", RegexOptions.Compiled | RegexOptions.Multiline);
private Process proc;
private IDictionary<int, long> cpuLoads;
internal CpuInfo()
{
cpuLoads = new Dictionary<int, long>();
proc = new Process();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.Arguments = "-u -P ALL 2";
proc.StartInfo.FileName = "mpstat";
proc.StartInfo.RedirectStandardOutput = true;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived += proc_OutputDataReceived;
proc.Start();
proc.BeginOutputReadLine();
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Match match = parser.Match(e.Data);
if (match.Success)
{
int cpuID = int.Parse(match.Groups["CpuID"].Value);
string idleValue = match.Groups["IdlePercentage"].Value.Replace(',', '.');
decimal idle = decimal.Parse(idleValue, CultureInfo.InvariantCulture);
cpuLoads[cpuID] = (long)((100m - idle) * 100);
}
}
~CpuInfo()
{
proc.OutputDataReceived -= proc_OutputDataReceived;
proc.Kill();
}
}