Mutexes and concurrent access in Go

Golang has mutexes (short for mutually exclusion) to manage concurrent access to a shared object via multiple goroutines. Here’s an example (taken from the excellent Go by Example: Mutexes). package main import ( "fmt" "sync" ) type Counter struct { mu sync.Mutex count map[string]int } func (c *Counter) Inc(name string) { c.mu.Lock() defer c.mu.Unlock() c.count[name]++ } func main() { cnt := Counter{count: map[string]int{"james": 0, "spartacus": 0}} var wg sync.WaitGroup increment := func(name string, n int) { for i := 0 ; i < n ; i++ { cnt.Inc(name) } wg.Done() } wg.Add(3) go increment("james", 100000) go increment("james", 100000) go increment("spartacus", 10000) wg.Wait() fmt.Println(cnt) } This code defines a simple Counter struct with a single method named Inc to increment the count map. Inc is responsible for managing the locking and unlocking of the mutex with c.mu.Lock() to lock it and defer c.mu.Unlock() to unlock it as the method is returning. ...

2022-07-22

Go routines and WaitGroups

Working with Goroutines is simple. You simply pre-prend your function call with go and you’re off and running. Here’s an example which calls the expensive() function five times before exiting. package main import ( "fmt" "time" ) func expensive(id int) { fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) // Oh, so expensive! fmt.Printf("Worker %d ending\n", id) } func main() { for i := 0; i < 5; i++ { go expensive(i) } fmt.Println("Exiting!") } But it has a problem… running the program shows output like this: ...

2022-07-18

Closures in Go

In Go, it’s pretty easy to write an anonymous function. package main import "fmt" func main() { func(s string){ fmt.Println("s is:", s) }("hello world") } Running the above code produces: $ go run main.go s is: hello world You can even assign the anonymous function to a variable like so: package main import "fmt" func main() { myFunc := func(s string){ fmt.Println("s is:", s) } myFunc("hello again, world!") } Now we get this output from the above code: ...

2022-07-13

Generics in Go 1.18

In Go 1.18, we get access to Genrics. Huzzah! Here’s an basic example of how to use them: package main import ( "fmt" ) func PrintAge[age float32 | int64](myAge age) { fmt.Printf("age: %s \t type: %T\n", myAge, myAge) } func main() { var age1 float32 = 30.5 var age2 int64 = 32 PrintAge(age1) PrintAge(age2) } Running the above program gives us the below output. You can see that it happily accepted the float32 and int64 values without issue. age: %!s(float32=30.5) type: float32 age: %!s(int64=32) type: int64 This saves us from having to either (a) define a separate PrintAge function for every type of number we want to accept or (b) writing the PrintAge to accept type interface{} which would then accept types we don’t want to accept (and having to mess with type assertions and all that). ...

2022-07-12

Go defer

defer in go is used to run a function later on. Specifically, the specified function will run when the enclosing function returns. There’s no clean equivalent to this in Python that I’ve found. The quintessential example is closing a file handle as your program exits. Example package main import ( "fmt" "os" ) func main() { file := createFile("/tmp/testingdefer.txt") defer closeFile(file) writeFile(file) } func createFile(path string) (file *os.File) { fmt.Println("creating file: ", path) file, _ = os.Create(path) // skipping error handling for brevity return } func writeFile(file *os.File) { fmt.Println("writing file") fmt.Fprintln(file, "here is some data for you!") } func closeFile(file *os.File) { fmt.Println("closing file") file.Close() // skipping error handling for brevity }

2021-07-20

Empty Go Interfaces

When declaring function parameters in Go, you must type the incoming data. Here’s an example: package main import "fmt" func print_stuff(s string) { fmt.Println(s) } func main() { print_stuff("Passing string!") } s string indicates that we’re passing a variable of type string into the function. This prevents us from from passing into variables of any other type. For example, this will fail with ./prog.go:10:17: cannot use 42 (type untyped int) as type string in argument to example_str if you try to pass an integer. ...

2021-07-11

Line beaks in file names on Linux

Today I accidentally created a file whose name had a newline character in it. I didn’t notice it when I did it, but here’s how it showed up in my shell. There was a directory whose name shared the first few characters of the filename. [22:07] james_simas@widget /var/tmp $ ls o* oops? <--- Suspicious file! oops: <--- Directory I tried to delete it: [22:07] james_simas@widget /var/tmp $ rm 'oops?' rm: oops?: No such file or directory Then I realized the my shell wasn’t interpreting the character right: ...

2021-02-02

Translating characters with tr

I recently was having to work with Ansible for several days to deploy changes globally across prod. I was storing my playbook limit in a file like so: [18:45] james_simas@widget ~/Downloads $ cat limit.txt host1.domain.com host2.domain.com host3.domain.com At one point, I needed to use supply the list of hostnames in BASH, but rather than having them be newline separated, I needed them to be separated by commas like this: host1.domain.com,host2.domain.com,host3.domain.com I didn’t want to modify my original files (I still needed them) and this seemed like a problem somebody else has problably solved. I Googled a bit and found an answer using tr. ...

2021-01-14

How to use extra_vars_path in the tower_job_template Ansible module

Today I was using Ansible’s tower_job_template module in a playbook. I needed to add extra variables to the job template, but Ansible’s public documentation of the the tower_job_template module had no examples of how to do this. I was unable to find any compelling examples on Google. I was a bit of poking around the Ansible API, it turns out this was far easier than I had anticipated. Here’s how use use extra_vars_path: ...

2020-03-19

Converting blog to Terraform & Ansible

Setting up my blog has always been easy. Previous to today, you just had to: Provision an Ubuntu 18 VM somewhere (I’ve been using Digital Ocean) Install Apache and remove its default website Use Hugo to generate the site’s static files and copy them to the VM host Done! But, never being one to sit still, I’ve decided to up my game and move to automatic provisioning with Terraform and Ansible. Why? Because it’s fucking cool, that’s why! ...

2019-10-05