I have a console application (dotnet core, ubuntu), that looks like following:
void Main()
{
try
{
var job = new Job();
job.Start();
while(Console.ReadLine() != "quit");
}
catch(Exception e)
{
//some handling
}
}
While Job
is implementation of JobAbstract
class:
public class JobAbstract
{
private readonly int _periodMs;
protected JobAbstract(int periodMs)
{
_periodMs = periodMs;
}
public bool Working { get; private set; }
public abstract Task Execute();
private void LogFatalError(Exception exception)
{
try
{
//logging
}
catch (Exception)
{
}
}
private async Task ThreadMethod()
{
while (Working)
{
try
{
await Execute();
}
catch (Exception exception)
{
LogFatalError(exception);
}
await Task.Delay(_periodMs);
}
}
public virtual void Start()
{
if (Working)
return;
Working = true;
Task.Run(async () => { await ThreadMethod(); });
}
public void Stop()
{
Working = false;
}
}
Job
is defined like:
public class Job : JobAbstract
{
public Job(periodMs) : base(periodMs)
{}
public override async Task Execute()
{
await SomeTask();
OtherKindOfJob();
await MaybeMoreAsyncTasks();
// etc
}
}
It all works fine, as you might expect.
Now, I'm wrapping it all up in docker containers for continuous delivery. docker stop
might be run on a container while Execute
method of Job
is run. In order to wait for the cycle to end and then exit gracefully, I've decided to use this approach:
public static void Main(string[] args)
{
var ended = new ManualResetEventSlim();
var starting = new ManualResetEventSlim();
AssemblyLoadContext.Default.Unloading += ctx =>
{
System.Console.WriteLine("Unloding fired");
starting.Set();
System.Console.WriteLine("Waiting for completion");
ended.Wait();
};
System.Console.WriteLine("Waiting for signals");
starting.Wait();
System.Console.WriteLine("Received signal gracefully shutting down");
Thread.Sleep(5000);
ended.Set();
}
I've tested it and it works, when calling docker stop
, docker daemon sends SIGTERM
signal to the process #1 of the container (which happens to be my app) and CoreCLR invokes AssemblyLoadContext.Default.Unloading
event which is handled appropriately.
So I have a working app and a way (theoretically) how to stop it. I'm just not sure, how should I implement it for a given context. Should I send some kind of token to the Job
? I want the run flow to be like following:
- The app is running normally.
SIGTERM
is received. IfExecute
isn't running, stop the app, if it is - wait for it to end.- When the
Execute
ends, end the app.
How this kind of thing should be implemented? Please advice some kind of scheme or pattern to achieve that. Thanks in advance!
So, I figured something out: what if I add public Task CurrentIteration { get; private set; }
and change Execute();
to:
CurrentIteration = Execute();
await CurrentIteration;
and after starting.Wait();
in stopping sample, add:
job.Stop();
await job.CurrentIteration;
Will that work? Are there any issues with the following approach?