aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2024-08-15 13:49:54 -0400
committerSam Anthony <sam@samanthony.xyz>2024-08-15 13:49:54 -0400
commit0940a4f4eaa39a19d842d757efa653e5cc69ac69 (patch)
tree2753e5a16395967637a063bac7c4486f8dfceed4
parent65829b44987283248c7b8707e3b05871cf5e4c46 (diff)
downloadgui-0940a4f4eaa39a19d842d757efa653e5cc69ac69.zip
mux: store last resize without mutex
-rw-r--r--mux.go29
-rw-r--r--srv.go52
2 files changed, 69 insertions, 12 deletions
diff --git a/mux.go b/mux.go
index 4198d61..2f93f15 100644
--- a/mux.go
+++ b/mux.go
@@ -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() {
diff --git a/srv.go b/srv.go
new file mode 100644
index 0000000..48cc3d3
--- /dev/null
+++ b/srv.go
@@ -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)
+}