Last active
August 8, 2022 00:35
-
-
Save christopher-wong/17355beb5121a7257266c136f3a3190e to your computer and use it in GitHub Desktop.
The Sleeping-Doctor Problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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