Skip to content

Instantly share code, notes, and snippets.

@Micrified
Last active March 10, 2024 18:26
Show Gist options
  • Save Micrified/2db3b405041fe15c752fa6e781b96208 to your computer and use it in GitHub Desktop.
Save Micrified/2db3b405041fe15c752fa6e781b96208 to your computer and use it in GitHub Desktop.
Generic channel-synchronised map (Golang)
// Note: Use mutexes people
package main
import (
"fmt"
"sync"
)
// generic type: response
type re [T comparable, U any] struct {
u U
ok bool
err error
}
// generic type: request
type rq [T comparable, U any] struct {
f func(*map[T]U) (U, bool, error) // function to execute
cre chan re[T,U] // response channel
}
// generic synchronous map
type Syncmap [T comparable, U any] struct {
m map[T]U
crq chan rq[T,U] // request channel
done chan any // stop channel
}
// insert into map
func (s *Syncmap[T,U]) Put (t T, u U) {
cre := make(chan re[T,U])
defer close(cre)
s.crq <- rq[T,U]{
f: func(m *map[T]U) (U, bool, error) {
(*m)[t] = u
return u, true, nil
},
cre: cre,
}
<-cre
}
// retrieve from map
func (s *Syncmap[T,U]) Get (t T) (U, bool) {
cre := make(chan re[T,U])
defer close(cre)
s.crq <- rq[T,U]{
f: func(m *map[T]U) (U, bool, error) {
u, ok := (*m)[t]
return u, ok, nil
},
cre: cre,
}
re := <-cre
return re.u, re.ok
}
// perform operation on map
func (s *Syncmap[T,U]) Do (f func(*map[T]U) (U, bool, error)) (U, bool, error) {
cre := make(chan re[T,U])
defer close(cre)
s.crq <- rq[T,U]{f: f, cre: cre}
re := <-cre
return re.u, re.ok, re.err
}
// setup a new syncmap with a go routine to attend to it
func Create [T comparable, U any] () *Syncmap[T,U] {
s := &Syncmap[T,U]{
m: map[T]U{},
crq: make(chan rq[T,U]),
done: make(chan any),
}
go func () {
defer close(s.crq)
for {
select {
case rq := <-s.crq: u, ok, err := rq.f(&s.m); rq.cre <- re[T,U]{u: u, ok: ok, err: err}
case <-s.done: return
}
}
}()
return s
}
// destroy the go routine handling the syncmap
func (s *Syncmap[T,U]) Delete () {
close(s.done)
}
func main() {
s := Create[string,int]()
defer s.Delete()
defer fmt.Println("Done")
// Instantiate the sum
s.Put("sum", 0)
// Launch a bunch of routines to use the map at the same time
var g sync.WaitGroup
g.Add(10)
for i := 0; i < 10; i++ {
go func (n int) {
defer g.Done()
s.Do(func(m *map[string]int) (int, bool, error) {
v, _ := (*m)["sum"]
(*m)["sum"] = v + n
return v, true, nil
})
}(i)
}
g.Wait()
// Print the sum
if sum, ok := s.Get("sum"); ok && sum == (10*(10-1))/2 {
fmt.Printf("%d\n", sum)
} else {
panic("bad mutual exclusion")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment