diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2024-08-21 19:25:17 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2024-08-21 19:25:55 -0400 |
| commit | 96df27214d149cc60dc08baae9d32f767fd032be (patch) | |
| tree | bdea4f92ffab720625ecbc77cd0f9d434a5dd271 | |
| parent | 5e81e13d2bd9de0827d1575a582224ce3b54819f (diff) | |
| download | gui-96df27214d149cc60dc08baae9d32f767fd032be.zip | |
explicit and graceful Env shutdown with attach/kill
| -rw-r--r-- | env.go | 9 | ||||
| -rw-r--r-- | kill.go | 75 |
2 files changed, 78 insertions, 6 deletions
@@ -8,8 +8,6 @@ import ( // Env is the most important thing in this package. It is an interactive graphical // environment, such as a window. // -// It has two channels: Events() and Draw(). -// // The Events() channel produces events, like mouse and keyboard presses, while the // Draw() channel receives drawing functions. A drawing function draws onto the // supplied draw.Image, which is the drawing area of the Env and returns a rectangle @@ -23,11 +21,10 @@ import ( // The Draw() channel may be synchronous. // // Drawing functions sent to the Draw() channel are not guaranteed to be executed. -// -// Closing the Draw() channel results in closing the Env. The Env will subsequently -// close the Events() channel. On the other hand, when the Events() channel gets closed -// the user of the Env should subsequently close the Draw() channel. type Env interface { Events() <-chan Event Draw() chan<- func(draw.Image) image.Rectangle + Killable + + killer } @@ -0,0 +1,75 @@ +package gui + +// A Killable object can be told to shut down by sending a signal via the Kill() channel. +// As its last action, the object posts a signal to Dead() and closes both channels, indicating that it has finished shutting down. +type Killable interface { + Kill() chan<- bool + Dead() <-chan bool +} + +// A killer can kill the object that is attached to it. +// The victim can attach itself to the killer by sending an attachMsg via the provided attach() channel. +// The attachMsg contains the victim itself and a `detach' channel. +// The victim can detatch itself from the killer by signalling over the `detach' channel. +// +// Only one victim can be attached to the killer at a time. +// Further messages sent on the attach() channel will block until the current victim is detached. +type killer interface { + attach() chan<- attachMsg +} + +// attachMsg is sent to a killer to attach the victim. +type attachMsg struct { + victim Killable + detach <-chan bool +} + +// attachHandler implements killer. It allows victims to attach themselves via the attach channel. +// There can only be one attached victim at a time. +// If attachHandler is killed while a victim is attached, it kills the victim. +// When killed, the victim must detach itself before dying. +type attachHandler struct { + attach chan<- attachMsg + kill chan<- bool + dead <-chan bool +} + +func newAttachHandler() attachHandler { + attach := make(chan attachMsg) + kill := make(chan bool) + dead := make(chan bool) + + go func() { + defer func() { + dead <- true + close(dead) + }() + defer close(kill) + defer close(attach) + + for { + var attached attachMsg + + select { + case attached = <-attach: + case <-kill: + return + } + + Attached: + for { + select { + case <-attached.detach: + break Attached + case <-kill: + attached.victim.Kill() <- true + <-attached.detach + <-attached.victim.Dead() + return + } + } + } + }() + + return attachHandler{attach, kill, dead} +} |