Last active
March 10, 2024 18:26
-
-
Save Micrified/2db3b405041fe15c752fa6e781b96208 to your computer and use it in GitHub Desktop.
Generic channel-synchronised map (Golang)
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
// 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