4

I have a number of console apps that need to be built that all run "Jobs". They will be long running processes - never ended, and they continually do work.

With that in mind, I'm hoping to build a generic way to host these jobs. Each will be a console app, but rather than using something like Console.Read() to stop the console app finishing, I was going to use ManualResetEventSlim to block the process from finishing.

I've come up with a generic interface that each console app will implement and then it can be run in a common way. As follows:

public interface IHostedProcess
{
    void Starting();
    void Run();
    void Stopping();
}

This means I can implement a common way to host processes with a start, run and stop method:

public class ProcessHost
{
    ManualResetEventSlim keepAliveEvent = new ManualResetEventSlim();

    public void Run(IHostedProcess hostedProcess)
    {
        hostedProcess.Starting();

        AssemblyLoadContext.Default.Unloading += async ctx =>
        {
            hostedProcess.Stopping();
            keepAliveEvent.Set();
        };

        hostedProcess.Run();
        keepAliveEvent.Wait();
    }
}

And a simple example of a class that implements the IHostedProcess interface and can be run in the console app:

public class MyHostedProcess : IHostedProcess
{
    public void Starting()
    {
        Console.WriteLine("Job starting");

        // Call my custom code here...
    }

    public void Run()
    {
        Console.WriteLine("Job running");

        // Call my custom code here...
    }

    public void Stopping()
    {
        Console.WriteLine("Job stopping1");

        // Call my custom code here...
    }
}

Leaving my console app code looking like this:

public static void Main(string[] args)
{
    try
    {
        var mp = new MyHostedProcess();

        var ph = new ProcessHost();
        ph.Run(mp);
    }
    catch (Exception e)
    {
        Console.WriteLine("Something went wrong! " + e.Message);
        throw;
    }
}

Output is as follows:

enter image description here

Then click the "X" to stop the process:

enter image description here

And we see the "stopping" message:

enter image description here

So this all works fine up until a certain point - when I try to carry out a long running shut down process in MyHostedProcess.Stopping it seems to have a hard shut down and not wait until that process is finished. Example, modify the "Stopping" process to this:

public void Stopping()
{
    Console.WriteLine("Job stopping first");

    // Call my custom code here...
    Thread.Sleep(20000); // Doesnt seem to wait this long...
    Console.WriteLine("Job stopping second"); // Never see this!
}

I never see "Job stopping second" output as the console app seems to hard stop after 10 seconds or so. What am I doing wrong? How to I handle the shutdown more gracefully and guarantee I can complete any shutdown work before the app closes? Maybe there is a way to do this "out of the box" that I've missed?

Thanks for any pointers in advance!

Rob
  • 6,819
  • 17
  • 71
  • 131
  • 1
    It probably has a timeout after SIGTERM was received. You probably only have ~10 seconds to do your shutdown logic (or possibly it works just fine if you do something other than sleep) – ikkentim Sep 05 '18 at 12:33
  • Tried functionality other than sleep - still no joy. Any way to work around the SIGTERM timeout? (maybe cancelling the SIGTERM?) – Rob Sep 05 '18 at 12:45
  • Worth pointing out that if I put a breakpoint in the Unloading callback and wait, it also hard closes the app – Rob Sep 05 '18 at 12:56
  • You need to hook into the console control handler. There is a similar [question and answers](https://stackoverflow.com/questions/474679/capture-console-exit-c-sharp) but be aware of the issues on Windows 7/8/10. You may need to implement a hidden window to handle all the different events e.g. `X`, `Ctrl-C`, Log-off, Shutdown etc. – Simply Ged Sep 06 '18 at 02:30
  • Surely DllImport for Kernal32 is a windows specific solution? I should have mentioned but my intent is to run this on a Linux machine, therefore this needs to be reliable on both OS's – Rob Sep 07 '18 at 07:16
  • Did you solve it? – Pavel Jedlicka Mar 15 '19 at 11:22

0 Answers0