package main
import (
"flag"
"fmt"
"sync"
)
var (
vol = flag.Int("v", 100, "volume")
max = flag.Int("m", 10000, "send numbers")
)
type msg struct {
Q int
A int
}
type comm struct {
Sch chan msg
Rch chan msg
}
type reply struct {
Ch chan *comm
}
var (
poolSub chan reply = make(chan reply)
poolAdd chan *comm = make(chan *comm)
)
var (
standbyPub chan reply = make(chan reply)
standbyOk chan *comm = make(chan *comm)
)
func save() {
var pool []*comm = make([]*comm, 0, *vol)
var standby []*comm = make([]*comm, 0, *vol)
for {
select {
case x := <-poolSub:
last := len(pool) - 1
if last > 0 {
x.Ch <- pool[last]
pool = pool[:last]
} else {
x.Ch <- nil
}
case x := <-poolAdd:
standby = append(standby, x)
case y := <-standbyPub:
last := len(standby) - 1
if last > 0 {
y.Ch <- standby[last]
standby = standby[:last]
} else {
y.Ch <- nil
}
case y := <-standbyOk:
pool = append(pool, y)
}
}
}
func set() func(func(msg) msg) {
var comms []comm
return func(g func(x msg) msg) {
for i := 0; i < *vol; i++ {
comms = append(comms, comm{})
comms[i].Sch = make(chan msg)
comms[i].Rch = make(chan msg)
}
for i := 0; i < *vol; i++ {
i := i
rch := comms[i].Sch
sch := comms[i].Rch
poolAdd <- &comms[i]
go func() {
for {
x := <-rch
sch <- g(x)
poolAdd <- &comms[i]
}
}()
}
}
}
const (
FIZZBUZZ = iota
FIZZ
BUZZ
NOTFIZZBUZZ
)
func init() {
flag.Parse()
go save()
f := set()
g := func(m msg) msg {
x := m.Q
switch {
case x%15 == 0:
m.A = FIZZBUZZ
case x%3 == 0:
m.A = FIZZ
case x%5 == 0:
m.A = BUZZ
default:
m.A = NOTFIZZBUZZ
}
return m
}
f(g)
}
var ctrl chan struct{} = make(chan struct{})
func let() {
close(ctrl)
}
func send(g func() msg) {
for i := 0; i < *max; i++ {
go func() {
res := make(chan *comm)
<-ctrl
for {
poolSub <- reply{res}
c := <-res
if c != nil {
c.Sch <- g()
break
}
}
close(res)
}()
}
}
// done is for waiting all-received-timing
var done chan struct{} = make(chan struct{})
func wait() {
<-done
}
func recv(f func(chan msg) error) {
var wg sync.WaitGroup
wg.Add(*max)
go func() {
for {
res := make(chan *comm)
standbyPub <- reply{res}
c := <-res
if c != nil {
go func() {
f(c.Rch)
wg.Done()
}()
standbyOk <- c
}
close(res)
}
}()
wg.Wait()
close(done)
}
func main() {
// send and g sends numbers to setted goroutines
f := func() func() msg {
var n int
return func() msg {
n++
return msg{Q: n}
}
}
g := f()
go send(g)
// recv and h processes the results from setted goroutines
h := func(ch chan msg) error {
m := <-ch
var ans string
switch m.A {
case FIZZBUZZ:
ans = "fizzbuzz"
case FIZZ:
ans = "fizz"
case BUZZ:
ans = "buzz"
case NOTFIZZBUZZ:
ans = "-"
}
fmt.Println(m.Q, ":", ans)
return nil
}
go recv(h)
let() // start
wait() // wait
}