Skip to content

Instantly share code, notes, and snippets.

@pbnjay
Created November 3, 2020 19:08
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 pbnjay/2aa3ad073391cedaab0be1b8228779cf to your computer and use it in GitHub Desktop.
Save pbnjay/2aa3ad073391cedaab0be1b8228779cf to your computer and use it in GitHub Desktop.
Sleeping Barbers
// I was inspired by @goinggodotnet to implement the sleeping barbers problem
// https://twitter.com/goinggodotnet/status/1323664013264375809
//
// https://en.wikipedia.org/wiki/Sleeping_barber_problem
package main
import (
"flag"
"log"
"math/rand"
"sync/atomic"
"time"
)
type Customer struct {
Name string
}
var Sleepytime = &Customer{"Napping..."}
func NewCustomer() *Customer {
firstName := firstNames[rand.Intn(len(firstNames))]
surName := surNames[rand.Intn(len(surNames))]
return &Customer{firstName + " " + surName}
}
func (c *Customer) CutFinished() {
log.Println(c.Name, "likes the haircut!")
atomic.AddUint32(&numCuts, 1)
}
func (c *Customer) Leave() {
log.Println(c.Name, "walked out the door")
atomic.AddUint32(&numLeft, 1)
}
type Barber struct {
Name string
cutting chan *Customer
}
func NewBarber() *Barber {
firstName := firstNames[rand.Intn(len(firstNames))]
surName := surNames[rand.Intn(len(surNames))]
return &Barber{
Name: firstName + " " + surName,
cutting: make(chan *Customer, 1),
}
}
func (b *Barber) Work(w *WaitingRoom) {
for {
c, ok := w.FindCustomer()
if !ok {
log.Println(b.Name, "is taking a nap")
b.cutting <- Sleepytime
} else {
log.Println(b.Name, "is cutting", c.Name)
b.cutting <- c
}
dur := time.Second * time.Duration(rand.Intn(5))
time.Sleep(dur)
// get out of the chair
<-b.cutting
if ok {
c.CutFinished()
c.Leave()
}
}
}
func (b *Barber) Checkin(c *Customer) bool {
select {
case b.cutting <- c:
return true
default:
return false
}
}
//////
type WaitingRoom struct {
seats chan *Customer
}
func NewWaitingRoom(nc int) *WaitingRoom {
return &WaitingRoom{seats: make(chan *Customer, nc)}
}
func (r *WaitingRoom) FindSeat(c *Customer) bool {
select {
case r.seats <- c:
// there's a chair free
log.Println(c.Name, "sat down in the waiting room")
return true
default:
// all the chairs are full
log.Println(c.Name, "left because the waiting room is full")
return false
}
}
func (r *WaitingRoom) FindCustomer() (*Customer, bool) {
select {
case c, ok := <-r.seats:
log.Println(c.Name, "called in from the waiting room")
// there was a customer waiting
return c, ok
default:
log.Println("no one in the waiting room")
// no one waiting
return nil, false
}
}
////////
type Shop struct {
barbers []*Barber
waitingRoom *WaitingRoom
}
func NewShop(numChairs, numBarbers int) *Shop {
s := &Shop{
barbers: make([]*Barber, 0, numBarbers),
waitingRoom: NewWaitingRoom(numChairs),
}
for i := 0; i < numBarbers; i++ {
b := NewBarber()
go b.Work(s.waitingRoom)
}
return s
}
func (s *Shop) GetHaircut(c *Customer) {
atomic.AddUint32(&numEntered, 1)
// check the barbers
for _, b := range s.barbers {
if b.Checkin(c) {
// got a cut right away!
return
}
}
if s.waitingRoom.FindSeat(c) {
// got a seat to wait
return
}
c.Leave()
}
////////
var (
numEntered, numCuts, numLeft uint32
)
func statsRunner() {
for range time.Tick(time.Second * 5) {
e := atomic.LoadUint32(&numEntered)
c := atomic.LoadUint32(&numCuts)
l := atomic.LoadUint32(&numLeft)
log.Printf("STATS: %d entered, %d left, %d cuts", e, l, c)
}
}
func main() {
numChairs := flag.Int("c", 4, "`number` of chairs in waiting room")
numBarbers := flag.Int("b", 1, "`number` of barbers")
flag.Parse()
s := NewShop(*numChairs, *numBarbers)
///////////
go statsRunner()
for {
// 0-5s random entry times
dur := time.Second * time.Duration(rand.Intn(5))
time.Sleep(dur)
c := NewCustomer()
go s.GetHaircut(c)
}
}
package main
// extracted from https://blogs.ancestry.com/cm/whats-the-most-popular-surname-in-your-state/
var surNames = []string{"Anderson", "Brown", "Chavez", "Garcia", "Hernandez",
"Johnson", "Jones", "Kim", "Lee", "Lopez", "Martinez", "Miller", "Nelson",
"Olson", "Smith", "Sullivan", "Williams", "Wong"}
// top 25 male and female names from 2000
// extracted from http://www.babynames.it/top100/1/year-2000.html
var firstNames = []string{
"Jacob", "Emily", // 1st
"Michael", "Hannah", // 2nd
"Matthew", "Madison", // 3rd
"Joshua", "Ashley", // 4th
"Christopher", "Sarah", // 5th
"Nicholas", "Alexis", // 6th
"Andrew", "Samantha", // 7th
"Joseph", "Jessica", // 8th
"Daniel", "Taylor", // 9th
"Tyler", "Elizabeth", // 10th
"William", "Lauren", // 11th
"Brandon", "Alyssa", // 12th
"Ryan", "Kayla", // 13th
"John", "Abigail", // 14th
"Zachary", "Brianna", // 15th
"David", "Olivia", // 16th
"Anthony", "Emma", // 17th
"James", "Megan", // 18th
"Justin", "Grace", // 19th
"Alexander", "Victoria", // 20th
"Jonathan", "Rachel", // 21st
"Christian", "Anna", // 22nd
"Austin", "Sydney", // 23rd
"Dylan", "Destiny", // 24th
"Ethan", "Morgan", // 25th
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment