Skip to main content
Tweeted twitter.com/StackSoftEng/status/808354241878392832
deleted 5 characters in body
Source Link
Robert Harvey
  • 200.7k
  • 55
  • 470
  • 683

EDIT

 

EDIT

 
added 385 characters in body
Source Link
nix
  • 543
  • 2
  • 6
  • 16

EDIT

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?

EDIT

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?

Source Link
nix
  • 543
  • 2
  • 6
  • 16

How to wait for the execution of method and then exit?

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:

  1. The app is running normally.
  2. SIGTERM is received. If Execute isn't running, stop the app, if it is - wait for it to end.
  3. 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!