34

Is there any API to let the main goroutine sleep forever?

In other words, I want my project always run except when I stop it.

John Weldon
  • 39,849
  • 11
  • 94
  • 127
Dev Zhou
  • 865
  • 2
  • 14
  • 21

3 Answers3

72

"Sleeping"

You can use numerous constructs that block forever without "eating" up your CPU.

For example a select without any case (and no default):

select{}

Or receiving from a channel where nobody sends anything:

<-make(chan int)

Or receiving from a nil channel also blocks forever:

<-(chan int)(nil)

Or sending on a nil channel also blocks forever:

(chan int)(nil) <- 0

Or locking an already locked sync.Mutex:

mu := sync.Mutex{}
mu.Lock()
mu.Lock()

Quitting

If you do want to provide a way to quit, a simple channel can do it. Provide a quit channel, and receive from it. When you want to quit, close the quit channel as "a receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received".

var quit = make(chan struct{})

func main() {
    // Startup code...

    // Then blocking (waiting for quit signal):
    <-quit
}

// And in another goroutine if you want to quit:
close(quit)

Note that issuing a close(quit) may terminate your app at any time. Quoting from Spec: Program execution:

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

When close(quit) is executed, the last statement of our main() function can proceed which means the main goroutine can return, so the program exits.

Sleeping without blocking

The above constructs block the goroutine, so if you don't have other goroutines running, that will cause a deadlock.

If you don't want to block the main goroutine but you just don't want it to end, you may use a time.Sleep() with a sufficiently large duration. The max duration value is

const maxDuration time.Duration = 1<<63 - 1

which is approximately 292 years.

time.Sleep(time.Duration(1<<63 - 1))

If you fear your app will run longer than 292 years, put the above sleep in an endless loop:

for {
    time.Sleep(time.Duration(1<<63 - 1))
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • Not working. The program crashes with `fatal error: all goroutines are asleep - deadlock!` – recolic Nov 25 '20 at 08:25
  • @recolic The answer shows how to block the main goroutine. If all your other goroutines block as well, that's obviously a deadlock. If you get that, you're doing something wrong in your other goroutines, or you shouldn't block unconditionally your main. – icza Nov 25 '20 at 08:28
  • I'm simply writing a program to trick windows defender. In other word, I'm implementing a deadlock. ( https://git.recolic.net/bensl/file-share/blob/master/fuck-windows-defender/main.go ) – recolic Nov 25 '20 at 08:29
  • @recolic I don't see your code, I obviously can't reason about it. If you have a specific question, post a new question. – icza Nov 25 '20 at 08:30
  • @recolic You have a single (main) gorotuine and you block that. What else would you expect? – icza Nov 25 '20 at 08:35
  • I'm working on replacing the WindowsDefender executable with this dead program, so WindowsDefender stop performing heavy IO operation while I'm gaming... – recolic Nov 25 '20 at 08:39
  • 1
    @recolic If you just want to wait indefinitely, use a big duration with sleep, e.g. `time.Sleep(time.Duration(1<<63 - 1))` – icza Nov 25 '20 at 08:43
8

It depends on use cases to choose what kind of sleep you want.

@icza provides a good and simple solution for literally sleeping forever, but I want to give you some more sweets if you want your system could shutdown gracefully.

You could do something like this:

func mainloop() {
    exitSignal := make(chan os.Signal)
    signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM)
    <-exitSignal

    systemTeardown()
}

And in your main:

func main() {
    systemStart()
    mainloop()
}

In this way, you could not only ask your main to sleep forever, but you could do some graceful shutdown stuff after your code receives INT or TERM signal from OS, like ctrl+C or kill.

nevets
  • 4,631
  • 24
  • 40
  • I think you should use a buffered channel `exitSignal := make(chan os.Signal, 1)`, otherwise it shows this error https://github.com/hlandau/service/issues/10 – kevin Feb 06 '23 at 20:14
0

Another solution to block a goroutine. This solution prevents Go-Runtime to complain about the deadlock:

import "time"

func main() {
    for {
        time.Sleep(1138800 * time.Hour)
    }
}
recolic
  • 554
  • 4
  • 18