In below program, What I am trying to achieve is when I press Ctrl+C or give SIGTERM then I want to make 'isEnable' false and let the while loop finish executing it's code one last time and exit the program gracefully.
I can achieve this only with CancelKeyPress because EventArgs passed on this handler has Cancel property and I just need to set it true. And, CancelKeyPress only handle Ctrl+C. But, I want same thing when I got SIGTERM signal.
Does anyone know is it possible to resuming the process after getting SIGTERM signal just like in CancelKeyPress?
Please let me know if you need more information to understand my question.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
bool isEnable = true;
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
isEnable = false; // making while loop false
Console.WriteLine("Inside CancelKeyPress");
};
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
isEnable = false; // making while loop false
Console.WriteLine("AppDomain ProcessExit!");
};
while (isEnable)
{
Console.WriteLine("first");
Thread.Sleep(3000);
Console.WriteLine("Second");
Thread.Sleep(3000);
Console.WriteLine("Third");
Thread.Sleep(3000);
Console.WriteLine("Fourth");
Thread.Sleep(3000);
}
Console.WriteLine("Gracefull Shotdown");
}
}
SIGTERM is a "polite" way of asking the program to terminate gracefully.
The program can then handle this is any way it wishes.
It can ignore it
It can shut down immediately
It can finish processing the current task, then gracefully
shutdown (disconnect from DB, close files etc).
It can wait as long as it wants and then shutdown.
(If running under Kubernetes then you can configure how long K8s will wait between sending a SIGTERM and a SIGKILL. So if your app needs 10 minutes to shut down then you can configure this and it is considered perfectly acceptable.)
In your code above, you are not "resuming processing" but are choosing to catch and handle this in your program by effectively ignoring the CTRL-C request.
So there is no reason why you can't do this also when receiving a SIGTERM on other platforms.
You might find this post helpful:
https://medium.com/#rainer_8955/gracefully-shutdown-c-apps-2e9711215f6d
Related
I have managed to get this down to a minimal test case running in VS2017 with .Net Framework 4.6:
static void Main(string[] args)
{
Console.CancelKeyPress += (o, e) => End(); //some attempt to exit gracefully
Task.Run(() => Task.Delay(100000)).Wait();
}
private static void End()
{
Console.WriteLine("EXITING...");
}
If I run this in the debugger with Console.CancelKeyPress commented out, CTRL+C force-terminates the application. With the code as written, it outputs "EXITING..." then hangs even though my event handler does nothing to prevent termination.
If I run from the command line, both versions exit as expected.
It took me some time to figure out the unfinished Task is involved but I have no idea why the event handler is changing the behaviour. Can anyone figure out why? Is it some debugger quirk? I see no errors in the debugger...
This is a visual studio debugger issue and I think it's happening because the visual studio is tracing all running threads separately and as you said you have an unfinished task running in another thread.
when you terminating your program from command-line using CTRL+C, the commandLine forcing your Main Thread to stop. this way your child thread will stop too but when you run your application using VS-debugger main thread is attached to the debugger.
by default, the debugger will release all blocking threads if you don't handle Console.CancelKeyPress event but not when you want to manually control terminate calls. (I'm not sure why)
if you want to force the debugger to terminate all threads you have to do this manually.
private static void End()
{
Console.WriteLine("EXITING...");
if (Debugger.IsAttached)
Environment.Exit(1); // this is equal to using CTRL+C in the terminal
}
also, you should use a cancelation token to cleanly cancel your running tasks before application termination.
class Program
{
private static CancellationTokenSource tokenSource;
static async Task Main(string[] args)
{
tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Console.CancelKeyPress += (o, e) => End();
await Task.Run(() => Task.Delay(100000), token);
}
private static void End()
{
Console.WriteLine("EXITING...");
tokenSource.Cancel();
tokenSource.Dispose();
if (Debugger.IsAttached)
Environment.Exit(1);
}
}
This is only a wild guess, but have you checked what happens when you set the Cancel property of the ConsoleCancelEventArgs instance e to false?
It would be weird if that would solve your problem, it may help narrow down the problem, though.
PS: I can't test it myself, as I'm currently on a Linux machine.
I have a service which creates a FileSystemWatcher inside OnStart and I want the service to end only after a file is created
So I have this code
protected void OnStart()
{
try
{
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
watcher.Path = WatchPath;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.txt";
watcher.IncludeSubdirectories = false;
watcher.Changed += OnCreated;
watcher.EnableRaisingEvents = true;
logEvents.Write(MyLogClass.LogLevel.Info, "Watcher created");
}
}
catch (Exception ex)
{
logEvents.Write(MyLogClass.LogLevel.Info, ex.Message);
}
if (finished)
{
OnStop();
}
}
The bool "finished" is only set to true in the last line of OnCreated
private void OnCreated(object sender, FileSystemEventArgs e)
{
//Here I do all the stuff I need and then:
finished = true;
}
But even when OnCreated is not executed the service ends and I get:
"The program - has exited with code 0 (0x0)."
I've tried calling OnStop() in different parts of the code but the same thing happens anyways. I'd like the service to stay still until a given file is created and to start over when OnCreated is done, but I don't know what's wrong in this.
A FileSystemWatcher, by itself, isn't enough to keep a process running. When no events are being serviced, no threads are required, and even when one is required it'll be a background thread.
What a process needs to stay running is a foreground thread. It doesn't matter what that thread is doing, so long as at least one exists.
It doesn't look like you have any useful work for such a thread to do - but you need one. I suggest you create a thread that just waits for a ManualResetEvent to become set. You then use that, rather than your finished variable, to indicate when you want the service to shut down.
At that point, the foreground thread exits, no foreground threads are left, the process shuts down and the service becomes stopped.
Note that you have to create a new thread for this waiting work. The thread on which OnStart (and OnStop) is called doesn't "belong" to you. It's a thread that services the Service Control Manager, and you need to let it do that.
Your code is executing once and then ending.
You need a loop with a sleep timer around the main code to keep it alive. And in here embed your check for finished.
Edit:
As mentioned in the comments.
I missed that you were doing this in OnStart. The loop shouldn't be done here otherwise windows will think that you service failed to start. So you will also need to refactor this.
I am trying to figure out a way to shutdown my foreground thread in the event that my application quits/terminates/etc..
From what I have tested and read about, it is to my understanding that the main thread is always last to execute. Is this always true?
If so, then could someone suggest of a way to graciously signal a shutdown of a foreground thread? (in the event of application quit) Or is this even possible?
I am kinda getting a feeling that a foreground thread should be responsible of shutting down itself (not relying on a outside signal), unless it is known that the process will not terminate/shutdown prematurely. Is this also true?
I have a couple of reasons for using a foreground thread instead of a background thread:
My thread allocates [ThreadStatic]native memory using Marshal.AllocHGlobal, and it needs to be properly released.
It is a server application and preferably it would send all the queued packets before shutting down (not essential).
For example:
volatile bool running = true;
static void Main()
{
AppDomain.CurrentDomain.ProcessExit += new
EventHandler(OnProcessExit);
var t = new Thread(ReadWrite);
t.Start();
ConsoleKeyInfo cki;
Console.WriteLine("Running..\n");
bool stopped = false;
while(!stopped)
{
// do server stuff..
.......
if (Console.KeyAvailable)
{
cki = Console.ReadKey(true);
if (cki.Key == ConsoleKey.X)
{
stopped = true;
}
}
}
}
private void ReadWrite()
{
while (running)
{
// do stuff....
....
Thread.Sleep(15);
}
FreeMemory();
}
public void EndServer()
{
FreeMemory();
running = false;
// do other stuff...
}
private void OnProcessExit(object sender, EventArgs e)
{
EndServer();
}
This results in:
stopped is made true
OnProcessExit is not called. (I have also tried explicitly calling EndServer() but got the same result
application hangs
So I suspect (but I am not sure) that since the main thread is last to execute, the program is waiting for ReadWrite to finish, which means what I am doing is not possible?
If it is not possible, I will either: Look to see if it is possible to do with background thread, or I will look into redesigning my native memory implementation.
It turns out I had no idea that native memory is freed when program is closed. Putting my thread in background will solve the rest of my issues.
Edit for future reference:
A background thread did not solve point 2, though it was not essential for me so I went ahead with this solution anyways.
Consider a Console application that starts up some services in a separate thread. All it needs to do is wait for the user to press Ctrl+C to shut it down.
I already tried some examples, but all examples work with "do while".
Consider this code :
static void Main(string[] args)
{
for (long ContactId = 0; ContactId < 1000; ContactId++)
{
try
{
Task.WaitAll(CreateJson(ContactId));
}
catch (Exception ex)
{
System.IO.File.AppendAllText("error.txt", ContactId + "\n", encoding);
}
}
Console.WriteLine("Finalizado em " + watch.Elapsed + "");
Console.ReadKey();
}
How can I do some "listener" to stop the all the process and exit the console?
I already tried this example but not works fine for me (How to keep a .NET console app running?)
Thanks
There's an event on Console class that will help you detect that user pressed control+c
Console.CancelKeyPress += myHandler;
void myHandler(object sender, ConsoleCancelEventArgs args)
{
// do something to cancel/interrupt your jobs/tasks/threads
}
This event is raised when user presses Ctrl+C or Break keys. IIRC, the event is invoked asynchronously from the Main() thread, so you will get it even if your "Main" function wandered deep into some other code and is grinding something.
I mean, if your main thread sleeps in Task.WaitAll, you will still get that event if keys are pressed.
Another thing is, how to cancel your tasks. That depends on how your long running tasks are organized. Probably CancellationTokenSource will be your friend if you use Tasks, Threads or some your own things. Tasks support CancellationTokens/Source. You can pass the token as one of arguments to the Factory or Task.Run or similar task-creating methods. However, you also need to ensure that the token.ThrowIfCancellationRequested() is used in the tasks' inner code. If you use some libraries to work for you, they may happen to not support CancellationToken, then you will need to research how to cancel/interrupt them properly.
I am having a rather weird problem I've been unable to Google out.
Working on some barely-sensical school demo program, I'm required to have a worker loop go about it's business, until it's interrupted by a CTRL-C signal, which then runs some other function. When that's done, the program resumes it's previous worker loop where it left off.
All that works beautifully, the new keypress event is done like this:
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += new ConsoleCancelEventHandler(interrupt);
When CTRL-C is pressed, the "interrupt" method is correctly called and executed... once.
When it's through, I appear to be completely unable to call it again via interrupt signal. Any subsequent CTRL-C keypress is completely ignored.
The "interrupt" method ends with .Join to merge with it's parent thread, since the event handlers are ran on seperate threads for some reason.
What is preventing me from calling the interrupt method as many times as I want?
Thread.Join doesn't "merge" threads. It blocks the current thread until the other thread is finished. As you are joining on the main thread, this will never happen until the program exits.
Just remove that call completely.
Are you setting the Cancel property to true appropriately? This works for me:
using System;
using System.Threading;
class Test
{
static void Main()
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (sender, args) => {
// We want to keep going...
args.Cancel = true;
Console.WriteLine("Handler called");
};
Console.WriteLine("Go for it!");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
It's not clear what your threading looks like - that could well be another aspect which is incorrect, as per Daniel's answer.
Console.TreatControlCAsInput looks like it should not be set to false:
true if Ctrl+C is treated as ordinary input; otherwise, false.