Search⌘ K
AI Features

Solution: Write Code with Livelock

Explore the concept of livelock in Go concurrency by examining a practical example involving two goroutines. Understand how livelock occurs when processes repeatedly relinquish resources to avoid conflicts. Learn to analyze the lock and unlock functions, use goroutines and WaitGroup effectively, and identify the cause of livelock, such as the delay from the Sleep function. This lesson helps you write code that handles livelock scenarios and improves concurrent program reliability.

Problem breakdown

Let’s walk through the challenge step by step. Livelock occurs when multiple processes constantly try to resolve an issue by backtracking, reverting, retrying, or rolling back. As per the problem statement, both spouses are overly courteous, and if the other spouse is hungry, they will pass over the spoon. Only two goroutines are involved.

Code walkthrough

Let’s walk through the code.

Go (1.6.2)
package main
import (
"fmt"
"sync"
"time"
"runtime"
)
func main() {
runtime.GOMAXPROCS(4)
type value struct {
sync.Mutex
id string
value1 string
value2 string
locked bool
}
lock := func(v *value) {
v.Lock()
v.locked = true
}
unlock := func(v *value) {
v.Unlock()
v.locked = false
}
move := func(wg *sync.WaitGroup, v1, v2 *value) {
defer wg.Done()
for i := 0; ; i++ {
if i >= 3 {
fmt.Println("canceling goroutine...")
return
}
fmt.Printf("%v: %v \n", v1.id, v1.value1)
lock(v1) // <1>
time.Sleep(2 * time.Second)
fmt.Printf("%v: Checking if %v \n", v1.id, v2.value2)
if v2.locked { // <2>
fmt.Printf("%v: Leaving spoon \n", v1.id)
unlock(v1) // <3>
continue
}
}
}
a, b := value{
id: "Alice",
value1: "I am picking up spoon",
value2: "Alice is hungry",
}, value{
id: "Bob",
value1: "I am picking up spoon",
value2: "Bob is hungry",
}
var wg sync.WaitGroup
wg.Add(2)
go move(&wg, &a, &b)
go move(&wg, &b, &a)
// go move(&wg, &a, &b)
// go move(&wg, &a, &b)
wg.Wait()
}

When we run the code and observe the output, we see that both Alice and Bob ...