Goroutines outlive their calling function

While watching a talk by Rob Pike I learned today something about Goroutines which surprised me: Goroutines outlive their calling function. Said another way, if the function which created the goroutine returns, the goroutine will continue running. (main() is the one exception.) This is fantastic! 🎉 Here’s an example of this in practice. package main import ( "fmt" "time" ) func person(msg string) <-chan string { // Function returns a receive-only channel ch := make(chan string) // Create unbuffered channel go func() { // This goroutine lives on after person() returns for i := 0; ; i++ { time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) ch <- fmt.Sprintf("%s %d", msg, i) } }() return ch } func main() { james := person("james") // Assign returned channel to variable sinah := person("sinah") for i := 0; i < 5; i++ { fmt.Println(<-james) // Block, waiting for value on channel fmt.Println(<-sinah) } fmt.Println("Done!") } Output: ...

2023-09-18

Getting the Git Commit in Go

Go 1.18 added a feature to easily get the Git commit version that the binary was built from. This is so much easier than the old way of doing it! 🎉 package main import ( "fmt" "runtime/debug" ) func main() { info, _ := debug.ReadBuildInfo() for _, setting := range info.Settings { if setting.Key == "vcs.revision" || setting.Key == "vcs.time" { fmt.Printf("%s:\t%s\n", setting.Key, setting.Value) } } } $ go build git-version $ ./git-version vcs.revision: 4d47dd39d1debbdf10715fd9ff967e09d4347f02 vcs.time: 2023-09-02T00:21:21Z More info here. warning For this to work, you must build with go build -o ./build/foo. Using go build -o ./build/foo/main.go will not work. ...

2023-09-01

Waiting for data from a Go channel with a timeout

The other day I ran into a situation where I wanted my function to block until it received data from a Go channel. But I didn’t want it to block indefinitely. I wanted it to timeout after a few seconds if it didn’t get any data. My coworker Dan showed me a technique I didn’t know about to easily accomplish this. It wasn’t surprising to learn how easy it was, given that Go has strong support for concurrency out the gate. ...

2023-09-01

Blocking with Channels in Go

Example 1 In the below code, <-messages will block until it gets something on the channel. This prevents main() from exiting until one item is received on the channel. package main import ( "fmt" "time" ) func waitThenSend(msg chan string) { time.Sleep(2 * time.Second) msg <- "badwolf" } func main() { messages := make(chan string) // make unbuffered channel go func() { waitThenSend(messages) // make goroutine, pass in channel }() <-messages // blocks until channel receives a value fmt.Println("done") } Example 2 The below example demonstrates using a sync.WaitGroupto keep the program running until all of the goroutines have exited. Data is sent to the function via the unbuffered channel named numbers. ...

2023-08-17

Easy templating with Python and Jinja

At work, I’ve been using both Golang and Ruby to render Nomad jobspec files in a consistent manner across environments. But Python is my one true love and I wanted to learn how to do it in Python. Turns out I already knew how… you can do it in Jinja2! I’ve used Jinja2 in the past a lot for Django and Ansible, so this was pretty easy to pick up. Here are some notes for future me when I want to inevitably do this again. ...

2023-07-04

Latency Numbers Every Programmer Should Know

These are useful and I’m putting them here so I can find them easily wherever I am. Latency Comparison Numbers (~2012) ---------------------------------- L1 cache reference 0.5 ns Branch mispredict 5 ns L2 cache reference 7 ns 14x L1 cache Mutex lock/unlock 25 ns Main memory reference 100 ns 20x L2 cache, 200x L1 cache Compress 1K bytes with Zippy 3,000 ns 3 us Send 1K bytes over 1 Gbps network 10,000 ns 10 us Read 4K randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD Read 1 MB sequentially from memory 250,000 ns 250 us Round trip within same datacenter 500,000 ns 500 us Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip Read 1 MB sequentially from disk 20,000,000 ns 20,000 us 20 ms 80x memory, 20X SSD Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms Notes ----- 1 ns = 10^-9 seconds 1 us = 10^-6 seconds = 1,000 ns 1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns Credit ------ By Jeff Dean: http://research.google.com/people/jeff/ Originally by Peter Norvig: http://norvig.com/21-days.html#answers Contributions ------------- 'Humanized' comparison: https://gist.github.com/hellerbarde/2843375 Visual comparison chart: http://i.imgur.com/k0t1e.png Source: https://gist.github.com/jboner/2841832 ...

2023-06-24

Python dataclasses are awesome!

Last week, I was working on some Python at work and I found myself wishing for an equivalent to Golang’s structs. Rather than passing in a bunch of core data types (string, dict, int, etc.) in an out of methods, I wanted to pass in a single object with predictable attributes. But…. I didn’t want to go through all the trouble of creating a class with a constructor, and passing in all the various attributes. ...

2023-05-22

nil maps in Golang

I’ve been a bit confused for a while over when a map in Golang gets created with a value of nil (its zero value) and when it does not, so I’m writing this to help me remember. Let’s look at various ways to initialize a map: package main import "fmt" func main() { m1 := map[string]string{} // initializes map m2 := make(map[string]string) // also initializes map var m3 map[string]string // does NOT initialize the map! HERE BE DRAGONS! fmt.Println(m1 == nil) // false fmt.Println(m2 == nil) // false fmt.Println(m3 == nil) // true m1["foo"] = "bar" m2["foo"] = "bar" m3["foo"] = "bar" // panics because the map is NOT initialized, i.e., map is still nil } My takeaways from this are: ...

2023-01-07

Thoughts on travel to Hawaii

My wife and I recently went to the Hawaiin islands for a few days for a vacatoin. Here’s what worked well and didn’t well about the trip. Resorts We stayed in resorts for the first time. This was new; normally we stay in Airbnbs or the like. Pros: Consistent experience. Clean and nice. Pools and hottubs. Safe. We felt very good leaving laptops and the like in the room. Cons: ...

2022-11-08

Built-in Go HTTP server

Go has a built-in HTTP server in net/http. Here’s me playing around using it: package main import ( "fmt" "net/http" "strings" ) func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") } func details(w http.ResponseWriter, req *http.Request) { resp := []string{} resp = append(resp, "Request Details:") resp = append(resp, fmt.Sprintf("- Request proto: %s", req.Proto)) resp = append(resp, fmt.Sprintf("- Headers:")) for name, val := range req.Header { resp = append(resp, fmt.Sprintf("\t- %s: %s", name, val)) } resp = append(resp, "\n") fmt.Fprintf(w, strings.Join(resp, "\n")) } func main() { http.HandleFunc("/hello", hello) http.HandleFunc("/details", details) fmt.Println("Started.") http.ListenAndServe("127.0.0.1:9999", nil) fmt.Println("Exiting.") } The /details endpoint returns interesting stuff about the HTTP request, like its protocol and headers: ...

2022-10-14