How to call deferred functions on SIGINT handling in Go

96 Views Asked by At

Is there a way to call the deferred functions in the signal handler function instead of re-implement it manually ?

Here is an example with multiple steps that take time where I tried to call the defer functions when the SIGINT is received

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    step1 := false
    step2 := false
    step3 := false

    signal_chan := make(chan os.Signal, 1)
    signal.Notify(signal_chan, syscall.SIGINT)
    go func() {
        <- signal_chan
        fmt.Println("SIGINT")
        // time.Sleep(10*time.Second)

        if step3 {
            defer3()
        }
        if step2 {
            defer2()
        }
        if step1 {
            defer1()
        }
        fmt.Println("alternate defer end")
        os.Exit(0)
    }()

    fmt.Println("step 1")
    defer defer1()
    step1 = true

    time.Sleep(2*time.Second)

    fmt.Println("step 2")
    defer defer2()
    step2 = true

    time.Sleep(2*time.Second)

    fmt.Println("step 3")
    defer defer3()
    step3 = true

    time.Sleep(2*time.Second)
    fmt.Println("the end")
}

func defer1() {
    fmt.Println("defer 1")
    time.Sleep(2*time.Second)
}

func defer2() {
    fmt.Println("defer 2")
    time.Sleep(2*time.Second)
}

func defer3() {
    fmt.Println("defer 3")
    time.Sleep(2*time.Second)
}

But it is a mess as the main function continue to run.

$ go run main.go
step 1
step 2
^CSIGINT
defer 2
step 3
defer 1
the end
defer 3
alternate defer end

The expected behavior

$ go run main.go
step 1
step 2
^CSIGINT
defer 2
defer 1
alternate defer end / the end

I guess I have to move the steps in a goroutine and stop it in the signal handler, right ?

1

There are 1 best solutions below

0
François On

I've found a simple solution by adding a condition to run the steps.

package main

import (
    "fmt"
    "os"
    "os/exec"
    "os/signal"
    "syscall"
)

func main() {
    stop := false
    signal_chan := make(chan os.Signal, 1)
    signal.Notify(signal_chan, syscall.SIGINT)
    go func() {
        <- signal_chan
        fmt.Println("SIGINT received, stopping")
        stop = true
    }()

    fmt.Println("step 1")
    defer defer1()
    exec.Command("sleep", "2").Run()

    if ! stop {
        fmt.Println("step 2")
        defer defer2()
        exec.Command("sleep", "60").Run()
    }

    if ! stop {
        fmt.Println("step 3")
        defer defer3()
        exec.Command("sleep", "2").Run()
    }

    fmt.Println("the end")
}

func defer1() {
    fmt.Println("defer 1")
    exec.Command("sleep", "2").Run()
}

func defer2() {
    fmt.Println("defer 2")
    exec.Command("sleep", "2").Run()
}

func defer3() {
    fmt.Println("defer 3")
    exec.Command("sleep", "2").Run()
}