I am writing a Rust program that does an action every 2 seconds. The program also uses the ctrlc crate to handle interrupts. However, in some cases, I have to wait up to 2 secs when killing the process. I am using the default std::thread::sleep function, which I seemingly cannot interrupt.
Here comes my question. Is it possible for me to make my program sleep in an interruptible way? In other words, does Rust have a default feature for interrupting a sleeping program? I know that C automatically interrupts any sleep function when the SIGINT signal is received.
Your signal handler needs to either make the running threads somehow stop or exit the process upon catching the signal.
Exitting the process is simple: call std::process::exit() in the signal handler after your cleanup is done.
Forwarding the signal to your sleeping threads is harder, you might be able to do that by setting some flag in an Arc<AtomicBool> upon catching SIGINT and repeatedly check that flag with shorter intermittent sleeps in between checks instead of a single longer sleep.
You could also come up with a solution based on channels where your ctrlc handler communicates with the running threads through a broadcast channel and waits for them to respond with some kind of done message before exitting the process. This done message could be through the sending end of a channel that the shutdown handler passes to the running thread. It could then wait with a timeout before force-quitting with std::process::exit.
If you only want to cleanup and exit, then you can just call std::process::exit in the signal handler as suggested by the other answer. However if you want your threads to stop so that your application can proceed to some other task, you can use a channel and recv_timeout to get an interruptible sleep:
use std::thread;
use std::time::Duration;
use std::sync::mpsc;
let (send, recv) = mpsc::channel();
thread::spawn(move || {
// When you want to sleep
if let Ok(_) = recv.recv_timeout (Duration::from_secs (2)) {
// Sleep was interrupted
return;
}
// Slept for 2s, proceeding
});
// And elsewhere when you want to interrupt the thread:
send.send(());
Related
If you have an #Async method the returns a CompletableFuture....and the future never gets completed, does spring just leak the thread? Yes, I know whomever is waiting on the results could timeout and assume exceptional completion for latter stages.....but that doesn't stop the thread. Even if you call cancel, it doesn't do shit to the running thread:
from the docs:
#param mayInterruptIfRunning this value has no effect in this
implementation because interrupts are not used to control
processing.
If I use Future instead of CompletableFuture, cancel will interrupt the thread. Unfortunately, there is no equivalent of "allOf" on Future like we have on CompletableFuture to wait for all the tasks, like so:
// wait for all the futures to finish, regardless of results
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
// if exceptions happened in any future, swallow them
// I don't care because I'm going to process each future in my list anyway
// we just wanted to wait for all the futures to finish
.exceptionally(ex -> null);
how are we supposed to cancel a thread that we bailed on?
if I didn't cancel it (somehow), would my pool just be down a thread forever????
Consider the following simple Rust program:
use std::time::Duration;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use ctrlc;
static running: AtomicBool = AtomicBool::new(true);
fn main() {
// Set up a thread that registers the sigint signal.
ctrlc::set_handler(|| {
running.store(false, Ordering::SeqCst);
});
// Loop as long as the signal has not been registered.
while running.load(Ordering::SeqCst) {
println!("Hello!");
thread::sleep(Duration::from_secs(10));
}
println!("Goodbye!");
}
It prints "Hello!" every ten seconds until someone press Ctrl+C, upon which it prints "Goodbye!" and exit. The problem is if Ctrl+C is pressed right after the thread goes to sleep. The user then has to wait for almost ten seconds until the program exits.
Is there any way to get around this, and somehow wake up the thread when the sigint signal is recieved? I'm prepared to change the ctrlc dependency for something else if it helps.
The only solution I have been able to come up with is to sleep during ten one second intervals instead, checking sigint before going back to sleep again at every wakeup. Is there a simpler and prettier way to do it?
As the doc says:
On Unix platforms, the underlying syscall may be interrupted by a spurious wakeup or signal handler. To ensure the sleep occurs for at least the specified duration, this function may invoke that system call multiple times. Platforms which do not support nanosecond precision for sleeping will have dur rounded up to the nearest granularity of time they can sleep for.
So, I propose you to use a more low level function directly, there is one crates that encapsule it shuteye, but I don't know if it's a good one.
I am writing a SDL multithread application. My application has some threads that waits for signal by using SDL_CondWait.
SDL_LockMutex(mutex);
SDL_CondWait(cond,mutex);
SDL_UnlockMutex(mutex);
When users exit, I want to wake up the threads to let the application exit. However, when I used SDL_CondSignal to signal the conditional variables, the application sometimes got errors.
SDL_CondSignal(cond);
I guessed that is because at that moment, the condition varialbe isn't waiting.
My question is how to check whether SDL condition variables is waiting ?
struct SDL_cond
{
SDL_mutex *lock;
int waiting;
int signals;
SDL_sem *wait_sem;
SDL_sem *wait_done;
};
The waiting struct field holds amount of threads that the are blocked.
Also, if you want to wake up all threads, you should call SDL_CondBroadcast. SDL_CondSignal wakes up only one of the threads.
I'm reading "Linux kernel development 3rd edition by Robert Love" to get a general idea about how the Linux kernel works..(2.6.2.3)
I'm confused about how wait queues work for example this code:
/* ‘q’ is the wait queue we wish to sleep on */
DEFINE_WAIT(wait);
add_wait_queue(q, &wait);
while (!condition) { /* condition is the event that we are waiting for */
prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE);
if (signal_pending(current))
/* handle signal */
schedule();
}
finish_wait(&q, &wait);
I want to know which process is running this code? is it a kernel thread? whose process time is this?
And also in the loop, while the condition is still not met we will continue sleeping and call schedule to run another process the question is when do we return to this loop?
The book says that when a process sleeps, it's removed from our run queue, else it would be waken and have to enter a busy loop...
Also says: "sleeping should always be handled in a loop that ensures that the condition for which the task is waiting has indeed occurred."
I just want to know in what context is this loop running?
Sorry if this is a stupid Question. I'm just having trouble seeing the big pic
Which process is running the code? The process that called it. I don't mean to make fun of the question but the gist is that kernel code can run in different contexts: Either because a system call led to this place, because it is in a interrupt handler, or because it is a callback function called from another context (such as workqueues or timer functions).
Since this example is sleeping, it must be in a context where sleeping is allowed, meaning it is executed in response to a system call or at least in a kernel thread. So the answer is the process time is taken from the process (or kernel thread) that called into this kernel code that needs to sleep. That is the only place where sleeping is allowed in the first place.
A certain special case are workqueues, these are explicitly for functions that need to sleep. Typical use would be to queue a function that needs to sleep from a context where sleeping is forbidden. In that case, the process context is that of one of the kernel worker threads designated to process workqueue items.
You will return to this loop when the wait_queue is woken up, which either sets one task waiting on the queue to runnable or all of them, depending on the wake_up function called.
The most important thing is, forget about this unless you are interested in the implementation details. Since many people got this wrong and it's basically the same thing everywhere it's needed, there have long been macros encapsulating the whole procedure. Look up wait_event(), that's how your example should really look like:
wait_event(q, condition);
As per your example... I added comments....
NOTE: while creating waiting queue by default it will be in sleep stat.
DEFINE_WAIT(wait); /* first wait ---> it the kernel global wait queue it is pointing */
add_wait_queue(q, &wait); /* first wait ---> it the kernel global wait queue it is pointing using add_wait_queue(q, &wait); ---> you are adding your own waiting queue (like appending linked list) */
while (!condition) {
/* condition is the event that we are waiting for */
/*condition --> Let's say you are getting data from user space in write method (using __get_user()) */
prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE);
/* This will wait when any wake_up_process() call will be generated having interrupt */
if (signal_pending(current))
/* This is continuously monitoring if any signal is pending on current CPU on which wait queue is running while not pending any signal generally used return -ERESTARTSYS; or "break" the loop if interrupts came exa., SIGINT or SIGKILL and finishes wait queue statement to check again /
/ handle signal */
schedule(); // Scheduling of wait queue
// Remove from global data structure
}
finish_wait(&q, &wait); //Finishing wait queue
I am using the Thread::Pool module in perl to parallelize some perl code. This process takes a while and occasionally I will kill it from the command line with a SIGINT. Doing so causes the program to end abruptly, as I expected. This leaves some messy temporary files around, so I'd like to install a signal handler. I did this:
sub INT_Handler{
#clean up code
exit(1);
}
$SIG{'INT'} = 'INT_handler';
before creating the thread pool and starting the threads. Now when I send the SIGINT, the worker threads that are running die, but the pool promptly launches another set workers to handle the next set of jobs and continues running. Why doesn't the call to exit in the signal handler exit the main thread? What do I need to stop the process from running?
Edited in response to mob's comment
** Further edit **
Here is an example I wrote up.
use Thread::Pool;
sub INT_handler{
print "Handler\n";
exit(1);
}
$SIG{'INT'}='INT_handler';
sub f{
print "Started a thread " . rand(10000) . "\n";
sleep(10);
}
my $pool;
my $submit = \&f;
if (0){
$pool = Thread::Pool->new({do=>'f', workers=>5});
$submit = sub{ $pool->job; }
}
for (my $i = 0; $i < 100; $i++){ $submit->(); }
$pool->shutdown if defined $pool;
with 0, I see the expected result
h:57 Sep 15 16:15:19> perl tp.pl
Started a thread 3224.83224635111
Handler
but with 1, this happens
h:57 Sep 15 16:14:56> perl tp.pl
Started a thread 5034.63673711853
Started a thread 9300.99967009486
Started a thread 1394.45532885478
Started a thread 3356.0428193687
Started a thread 1424.4741558014
etc and the handler doesn't get entered and the process continues running. I had to kill the process with a signal other than SIGINT. Without the handler, both cases simply exit when passed a SIGINT.
This is more a hint rather than a definitive answer, but it appears your main thread is never in the "safe" state to run the signal handler. It does work when you enable Perl's unsafe signals:
PERL_SIGNALS=unsafe perl tp.pl
See perlipc for more information on safe and unsafe signals -- maybe it will lead you in the right direction to implement it with safe signals (as it probably should be).
(update by mob) Building on Michal's original insight, this workaround with Perl::Unsafe::Signals also gets the handler to work as you'd expect
use Perl::Unsafe::Signals;
...
UNSAFE_SIGNALS {
$pool->shutdown if defined $pool;
};
So clearly it is something about Perl's safe signalling mechanism that is interfering with the signal on its way to the handler. I wonder if this would be fixed by putting an UNSAFE_SIGNALS { ... } block inside of Thread::Pool::shutdown. Either way, I would file a bug report about this.