1

I use a 'watchdog' function to perform a failsafe wakeup if a certain command hangs. This is implemented by running a background task that sleeps for 30s and then executes the wakeup. If the parent command succeeds before the timeout period, the script kills the watchdog.

When piping the function (such as to sed to format its output), it hangs for the sleep period even after kill is invoked on the background task. What gives?

Here's a MCVE:

#! /bin/bash

function indent () { "$@" 2>&1 | sed 's/^/    /'; }

function sleeper () { sleep 1 && echo "Sleep complete."; }

function child () {
        sleeper &
        # ... do something that might hang here ...
        local spid=$!                                                                                                           echo "Invoked sleeper." | grep "sleep"
        kill $spid                                                                                                              
        echo "Done child."
}

indent child

The process will sleep one second even though the sleeper has already been killed.

2
  • I think the terms you use are a bit confusing, could you clarify what in your example script is "a certain command", "wakeup", "parent command" and "watchdog"? Aug 13, 2019 at 16:48
  • 1
    @JonasBerlin - I left out the actual use case it isn't relevant, but it's dmsetup udevcomplete $cookie - it would be in the sleeper function in place of the echo and would complete a dmsetup create table command (that would be called in the script) if it hangs for more than x seconds. The sleeper is called a watchdog because it checks if another thread hangs and acts accordingly. The foreground thread kills the watchdog if the command actually completed successfully. Aug 13, 2019 at 18:27

1 Answer 1

1

Slightly modified to kill the sleep process whose parent PID is $spid:

#! /bin/bash

function indent () { "$@" | sed 's/^/    /'; }

function sleeper () { sleep 1 && echo "Sleep complete."; }

function child () {
        sleeper &
        local spid=$!
        echo "Invoked sleeper." | grep "sleep"
        pkill -P "$spid" sleep
        echo "Done child."
}

indent child

I believe that the issue is that the background job, while running sleep, does not actually receive the TERM signal that you send it until sleep has terminated. The above pkill command will signal the sleep process instead of the background job itself.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .