Skip to content

Instantly share code, notes, and snippets.

@christopher-wong
Last active August 8, 2022 00:35
Show Gist options
  • Save christopher-wong/17355beb5121a7257266c136f3a3190e to your computer and use it in GitHub Desktop.
Save christopher-wong/17355beb5121a7257266c136f3a3190e to your computer and use it in GitHub Desktop.
The Sleeping-Doctor Problem
package main
import (
"context"
"log"
"sync"
"time"
)
const (
waitingRoomSize int64 = 5
patientAppointmentDuration time.Duration = 250 * time.Millisecond
)
type Appointment struct {
Name string
Duration time.Duration
}
/*
The Sleeping-Doctor Problem
https://en.wikipedia.org/wiki/Sleeping_barber_problem
A doctor's office consists of a waiting room with n chairs and the doctor's
exam room. If there are no patients to be seen, the doctor goes to sleep.
If a patient enters the doctor's office and all chairs are occupied, then
the patient leaves the office. If the doctor is busy but chairs are available,
then the patient sits in one of the free chairs. If the doctor is asleep,
the patient must page the doctor.
*/
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// PROMPT: how does a WaitGroup work?
// https://go.dev/src/sync/waitgroup.go
var wg sync.WaitGroup
pageDoctorCh := make(chan struct{})
waitingRoomCh := make(chan *Appointment, waitingRoomSize)
go handlePatients(ctx, &wg, pageDoctorCh, waitingRoomCh)
addPatient(ctx, &wg, "Kendall", waitingRoomCh, pageDoctorCh)
addPatient(ctx, &wg, "Logan", waitingRoomCh, pageDoctorCh)
addPatient(ctx, &wg, "Roman", waitingRoomCh, pageDoctorCh)
addPatient(ctx, &wg, "Tom", waitingRoomCh, pageDoctorCh)
addPatient(ctx, &wg, "Greg", waitingRoomCh, pageDoctorCh)
// PROMPT: what's the difference between `<-time.After` and `time.Sleep`
// After = blocking sub-state, Sleep= sleeping sub-state
// https://go101.org/article/unofficial-faq.html#time-sleep-after
// PROMPT: can you explain the difference between running, blocking, and asleep?
// https://go101.org/article/control-flows-more.html#states-of-goroutine
// an arbitrary wait before the next patient enters the waiting room
<-time.After(2000 * time.Millisecond)
addPatient(ctx, &wg, "Siobhan", waitingRoomCh, pageDoctorCh)
wg.Wait()
// PROMPT: how would you find a deadlock?
// https://yourbasic.org/golang/detect-deadlock/
}
func addPatient(ctx context.Context, wg *sync.WaitGroup, name string, waitingRoom chan<- *Appointment, pageDoctorCh chan<- struct{}) {
wg.Add(1)
a := &Appointment{
Name: name,
Duration: patientAppointmentDuration,
}
select {
case waitingRoom <- a:
log.Printf("%s entered waiting room", a.Name)
select {
case pageDoctorCh <- struct{}{}:
log.Printf("%s paging doctor", a.Name)
default:
}
default:
log.Printf("%s leaving office, full", a.Name)
return
}
}
func handlePatients(ctx context.Context, wg *sync.WaitGroup, pageDoctorCh <-chan struct{}, waitingRoomCh <-chan *Appointment) {
for {
select {
case <-ctx.Done():
log.Println("Closing office")
case a := <-waitingRoomCh:
log.Printf("Treating %s", a.Name)
<-time.After(a.Duration)
wg.Done()
default:
log.Println("No patients in waiting room, sleeping")
<-pageDoctorCh
log.Println("Paged by patient, waking up")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment