diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2024-08-15 13:49:54 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2024-08-15 13:49:54 -0400 |
| commit | 0940a4f4eaa39a19d842d757efa653e5cc69ac69 (patch) | |
| tree | 2753e5a16395967637a063bac7c4486f8dfceed4 | |
| parent | 65829b44987283248c7b8707e3b05871cf5e4c46 (diff) | |
| download | gui-0940a4f4eaa39a19d842d757efa653e5cc69ac69.zip | |
mux: store last resize without mutex
| -rw-r--r-- | mux.go | 29 | ||||
| -rw-r--r-- | srv.go | 52 |
2 files changed, 69 insertions, 12 deletions
@@ -10,10 +10,12 @@ import ( // create multiple virtual Envs that all interact with the root Env. They receive the same // events and their draw functions get redirected to the root Env. type Mux struct { - mu sync.Mutex - lastResize Event - eventsIns []chan<- Event - draw chan<- func(draw.Image) image.Rectangle + sizeSrv server[image.Rectangle] + + mu sync.Mutex + eventsIns []chan<- Event + + draw chan<- func(draw.Image) image.Rectangle } // NewMux creates a new Mux that multiplexes the given Env. It returns the Mux along with @@ -21,9 +23,12 @@ type Mux struct { // closing the Draw() channel on the master Env closes the whole Mux and all other Envs // created by the Mux. func NewMux(env Env) (mux *Mux, master Env) { + sizeSrv := newServer[image.Rectangle]() drawChan := make(chan func(draw.Image) image.Rectangle) - mux = &Mux{draw: drawChan} - master = mux.makeEnv(true) + mux = &Mux{ + sizeSrv: sizeSrv, + draw: drawChan, + } go func() { for d := range drawChan { @@ -33,11 +38,12 @@ func NewMux(env Env) (mux *Mux, master Env) { }() go func() { + defer sizeSrv.close() for e := range env.Events() { - mux.mu.Lock() if resize, ok := e.(Resize); ok { - mux.lastResize = resize + sizeSrv.update <- resize.Rectangle } + mux.mu.Lock() for _, eventsIn := range mux.eventsIns { eventsIn <- e } @@ -50,6 +56,7 @@ func NewMux(env Env) (mux *Mux, master Env) { mux.mu.Unlock() }() + master = mux.makeEnv(true) return mux, master } @@ -75,12 +82,10 @@ func (mux *Mux) makeEnv(master bool) Env { mux.mu.Lock() mux.eventsIns = append(mux.eventsIns, eventsIn) + mux.mu.Unlock() // make sure to always send a resize event to a new Env if we got the size already // that means it missed the resize event by the root Env - if mux.lastResize != nil { - eventsIn <- mux.lastResize - } - mux.mu.Unlock() + eventsIn <- Resize{mux.sizeSrv.get()} go func() { func() { @@ -0,0 +1,52 @@ +package gui + +// server is a concurrent interface that stores a value and serves it to clients. +// The stored value can be changed by sending the new value via update. server blocks +// until the first value is received. +// +// A client can read the stored value by sending a channel via request. The server +// responds by sending the value back via the channel. The client is responsible for +// closing the channel. +// +// A server should be closed after use. +type server[T any] struct { + request chan<- chan T + update chan<- T +} + +func newServer[T any]() server[T] { + request := make(chan chan T) + update := make(chan T) + go func() { + val := <-update // wait for initial value + for { + select { + case v, ok := <-update: + if !ok { // closed + return + } + val = v + case req, ok := <-request: + if !ok { // closed + return + } + go func() { // don't wait for client to receive + req <- val + }() + } + } + }() + return server[T]{request, update} +} + +// get makes a synchronous request to the server and returns the stored value. +func (srv server[T]) get() T { + c := make(chan T) + srv.request <- c + return <-c +} + +func (srv server[T]) close() { + close(srv.request) + close(srv.update) +} |