diff --git a/LittleBookOfSemaphores/README.md b/LittleBookOfSemaphores/README.md index 1c71955..2104326 100644 --- a/LittleBookOfSemaphores/README.md +++ b/LittleBookOfSemaphores/README.md @@ -1,7 +1,7 @@ # The Little Book of Semaphores Repository contains solutions, implemented in several programming languages, -to the problems presented by the book Little Book of Semaphores. +to the problems presented by [The Little Book of Semaphores](https://greenteapress.com/semaphores/LittleBookOfSemaphores.pdf). # Golang Solutions diff --git a/LittleBookOfSemaphores/chapter5/barbershop/README.md b/LittleBookOfSemaphores/chapter5/barbershop/README.md new file mode 100644 index 0000000..84d0777 --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/barbershop/README.md @@ -0,0 +1,18 @@ +# 5.2 The barbershop problem + +A barbershop consists of a waiting room with n chairs, and the +barber room containing the barber chair. If there are no customers +to be served, the barber goes to sleep. If a customer enters the +barbershop and all chairs are occupied, then the customer leaves +the shop. If the barber is busy, but chairs are available, then the +customer sits in one of the free chairs. If the barber is asleep, the +customer wakes up the barber. Write a program to coordinate the +barber and the customers. + +To make the problem a little more concrete, I added the following information: +* Customer threads should invoke a function named *getHairCut*. +* If a customer thread arrives when the shop is full, it can invoke *balk*, +which does not return. +* The barber thread should invoke *cutHair*. +* When the barber invokes cutHair there should be exactly one thread +invoking *getHairCut* concurrently. \ No newline at end of file diff --git a/LittleBookOfSemaphores/chapter5/barbershop/go/barber.go b/LittleBookOfSemaphores/chapter5/barbershop/go/barber.go new file mode 100644 index 0000000..38f5736 --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/barbershop/go/barber.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +type Barber struct { + MaxSleepMs int +} + +func (b *Barber) Work(sits chan *Customer) { + for c := range sits { + c.Ready.Done() + b.cutHair(c) + } +} + +func (b *Barber) cutHair(c *Customer) { + fmt.Printf("Barber is cutting the hair of Customer (%d)\n", c.Id) + RandomSleep(b.MaxSleepMs) + fmt.Printf("Barber finished the hair cut of Customer (%d)\n", c.Id) +} diff --git a/LittleBookOfSemaphores/chapter5/barbershop/go/customer.go b/LittleBookOfSemaphores/chapter5/barbershop/go/customer.go new file mode 100644 index 0000000..b1a39ae --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/barbershop/go/customer.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "sync" +) + +type Customer struct { + Id int + Lock *sync.Mutex + Ready *sync.WaitGroup +} + +func (c *Customer) BarberArrival(sits chan *Customer, cap int) { + c.Lock.Lock() + if len(sits) < cap { + fmt.Printf("Customer (%d) will wait in the chairs\n", c.Id) + c.Ready = &sync.WaitGroup{} + c.Ready.Add(1) + sits <- c + c.Lock.Unlock() + + c.Ready.Wait() + c.getHairCut() + } else { + c.Lock.Unlock() + c.balk() + } +} + +func (c *Customer) balk() { + fmt.Printf("Customer (%d) balked the wait for the hair cut\n", c.Id) +} + +func (c *Customer) getHairCut() { + fmt.Printf("Customer (%d) is getting a hair cut\n", c.Id) +} \ No newline at end of file diff --git a/LittleBookOfSemaphores/chapter5/barbershop/go/main.go b/LittleBookOfSemaphores/chapter5/barbershop/go/main.go new file mode 100644 index 0000000..717517b --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/barbershop/go/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "math/rand" + "sync" + "time" +) + +func main() { + n := 3 + sits := make(chan *Customer, n) + + barber := &Barber{MaxSleepMs: 10} + go barber.Work(sits) + + lock := &sync.Mutex{} + for i := 0; i < 20; i++ { + customer := &Customer{ + Id: i + 1, + Lock: lock, + } + go customer.BarberArrival(sits, n) + time.Sleep(time.Duration(2) * time.Millisecond) + } + time.Sleep(time.Duration(5) * time.Second) +} + +func RandomSleep(maxMs int) { + sleep := time.Duration(rand.Intn(maxMs)+1) * time.Millisecond + time.Sleep(sleep) +} \ No newline at end of file diff --git a/LittleBookOfSemaphores/chapter5/dining-savages/README.md b/LittleBookOfSemaphores/chapter5/dining-savages/README.md new file mode 100644 index 0000000..1c085f1 --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/dining-savages/README.md @@ -0,0 +1,13 @@ +# 5.1 Dining Savages + +A tribe of savages eats communal dinners from a large pot that +can hold M servings of stewed missionary. When a savage wants to +eat, he helps himself from the pot, unless it is empty. If the pot is +empty, the savage wakes up the cook and then waits until the cook +has refilled the pot. + +The synchronization constraints are: +* Savages cannot invoke getServingFromPot if the pot is empty. +* The cook can invoke putServingsInPot only if the pot is empty. + +Puzzle: Add code for the savages and the cook that satisfies the synchronization constraints. \ No newline at end of file diff --git a/LittleBookOfSemaphores/chapter5/dining-savages/go/cooker.go b/LittleBookOfSemaphores/chapter5/dining-savages/go/cooker.go new file mode 100644 index 0000000..8d06b71 --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/dining-savages/go/cooker.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "sync" + "sync/atomic" +) + +type Cooker struct { + M int + Start chan bool +} + +func (c *Cooker) Run(emptyPot, fullPot *sync.WaitGroup, servings *int32) { + c.Start <- true + for { + emptyPot.Add(1) + emptyPot.Wait() + c.serve(servings) + fullPot.Done() + } +} + +func (c *Cooker) serve(servings *int32) { + fmt.Printf("Cooker serving (%d) meals\n", c.M) + atomic.SwapInt32(servings, int32(c.M)) + fmt.Printf("Cooker served (%d) meals\n", c.M) +} diff --git a/LittleBookOfSemaphores/chapter5/dining-savages/go/main.go b/LittleBookOfSemaphores/chapter5/dining-savages/go/main.go new file mode 100644 index 0000000..47f39a8 --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/dining-savages/go/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "math/rand" + "sync" + "time" +) + +func main() { + m := 5 // serving meals + s := 10 // savages + + ready := make(chan bool, 1) + cooker := &Cooker{M: m, Start: ready} + savages := make([]*Savage, s) + lock := &sync.Mutex{} + for i := range savages { + savages[i] = &Savage{ + Id: i + 1, + MaxSleepMs: 1000, + Lock: lock, + } + } + + servings := int32(0) // initially zero servings + emptyPot := &sync.WaitGroup{} + fullPot := &sync.WaitGroup{} + + go cooker.Run(emptyPot, fullPot, &servings) + + <-ready + for _, savage := range savages { + go savage.Run(emptyPot, fullPot, &servings) + } + + time.Sleep(time.Duration(15) * time.Second) +} + +func RandomSleep(maxMs int) { + sleep := time.Duration(rand.Intn(maxMs)+1) * time.Millisecond + time.Sleep(sleep) +} \ No newline at end of file diff --git a/LittleBookOfSemaphores/chapter5/dining-savages/go/savage.go b/LittleBookOfSemaphores/chapter5/dining-savages/go/savage.go new file mode 100644 index 0000000..4276dbb --- /dev/null +++ b/LittleBookOfSemaphores/chapter5/dining-savages/go/savage.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "sync" + "sync/atomic" +) + +type Savage struct { + Id int + MaxSleepMs int + Lock *sync.Mutex +} + +func (s *Savage) Run(emptyPot, fullPot *sync.WaitGroup, servings *int32) { + for { + s.Lock.Lock() + if atomic.LoadInt32(servings) == 0 { + fullPot.Add(1) + emptyPot.Done() + fullPot.Wait() + } + atomic.AddInt32(servings, -1) + s.Lock.Unlock() + s.eat() + } +} + +func (s *Savage) eat() { + fmt.Printf("Savage (%d) is eating\n", s.Id) + RandomSleep(s.MaxSleepMs) + fmt.Printf("Savage (%d) finished eating\n", s.Id) +}