Skip to content

Instantly share code, notes, and snippets.

@feyeleanor
Created May 30, 2014 12:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save feyeleanor/ffd6d6ed5b32184c7281 to your computer and use it in GitHub Desktop.
Save feyeleanor/ffd6d6ed5b32184c7281 to your computer and use it in GitHub Desktop.
Goroutine launch puzzler
package main
import (
. "fmt"
. "net/http"
)
const ADDRESS = ":1024"
const SECURE_ADDRESS = ":1025"
func main() {
message := "hello world"
HandleFunc("/hello", func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Type", "text/plain")
Fprintf(w, message)
})
Spawn(
func() { ListenAndServe(ADDRESS, nil) },
func() { ListenAndServeTLS(SECURE_ADDRESS, "cert.pem", "key.pem", nil) },
)
}
func Spawn(f ...func()) {
done := make(chan bool)
for _, s := range f {
go func() {
s()
done <- true
}()
}
for l := len(f); l > 0; l-- {
<- done
}
}
package main
import (
. "fmt"
. "net/http"
)
const ADDRESS = ":1024"
const SECURE_ADDRESS = ":1025"
func main() {
message := "hello world"
HandleFunc("/hello", func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Type", "text/plain")
Fprintf(w, message)
})
Spawn(
func() { ListenAndServe(ADDRESS, nil) },
func() { ListenAndServeTLS(SECURE_ADDRESS, "cert.pem", "key.pem", nil) },
)
}
func Spawn(http, https func()) {
done := make(chan bool)
go func() {
http()
done <- true
}()
go func() {
https()
done <- true
}()
<- done
<- done
}
@feyeleanor
Copy link
Author

So, with these two programs the aim is to launch two goroutines and block program exit until they're both completed. In one goroutine we're launching an HTTP server, and in the other an HTTPS server.

In the version which works both servers launch and respond to traffic from a web browser.

In the version which doesn't work, the HTTP server completes as soon as the HTTPS server launches. The HTTPS server then responds to traffic from a web browser.

If I reverse the order of the passed functions, it's the HTTPS server which terminates and the HTTP server which works. So in other words, whichever server is launched first will terminate whilst the second server will run normally.

Any ideas on why the two pieces of code are behaving differently when they're apparently doing the same thing?

@wlaurance
Copy link

This took me a little while to figure out. The issue is that the function doesn't have a closure over the function s.

Try changing the body of the for loop in "This doesn't" to something like this.

  for _, s := range f {
    a := func(fun func()) {
      fun()
      done <- true
    }
    go a(s)
  }

Go doesn't capture external variables.

Here is a better explanation that I found https://code.google.com/p/go-wiki/wiki/CommonMistakes

Edit: Changed i back to _

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment