mirror of https://github.com/fatedier/frp
fatedier
9 years ago
2 changed files with 136 additions and 0 deletions
@ -0,0 +1,73 @@
|
||||
package broadcast |
||||
|
||||
type Broadcast struct { |
||||
listeners []chan interface{} |
||||
reg chan (chan interface{}) |
||||
unreg chan (chan interface{}) |
||||
in chan interface{} |
||||
stop chan int64 |
||||
stopStatus bool |
||||
} |
||||
|
||||
func NewBroadcast() *Broadcast { |
||||
b := &Broadcast{ |
||||
listeners: make([]chan interface{}, 0), |
||||
reg: make(chan (chan interface{})), |
||||
unreg: make(chan (chan interface{})), |
||||
in: make(chan interface{}), |
||||
stop: make(chan int64), |
||||
stopStatus: false, |
||||
} |
||||
|
||||
go func() { |
||||
for { |
||||
select { |
||||
case l := <-b.unreg: |
||||
// remove L from b.listeners
|
||||
// this operation is slow: O(n) but not used frequently
|
||||
// unlike iterating over listeners
|
||||
oldListeners := b.listeners |
||||
b.listeners = make([]chan interface{}, 0, len(oldListeners)) |
||||
for _, oldL := range oldListeners { |
||||
if l != oldL { |
||||
b.listeners = append(b.listeners, oldL) |
||||
} |
||||
} |
||||
|
||||
case l := <-b.reg: |
||||
b.listeners = append(b.listeners, l) |
||||
|
||||
case item := <-b.in: |
||||
for _, l := range b.listeners { |
||||
l <- item |
||||
} |
||||
|
||||
case _ = <-b.stop: |
||||
b.stopStatus = true |
||||
break |
||||
} |
||||
} |
||||
}() |
||||
|
||||
return b |
||||
} |
||||
|
||||
func (b *Broadcast) In() chan interface{} { |
||||
return b.in |
||||
} |
||||
|
||||
func (b *Broadcast) Reg() chan interface{} { |
||||
listener := make(chan interface{}) |
||||
b.reg <- listener |
||||
return listener |
||||
} |
||||
|
||||
func (b *Broadcast) UnReg(listener chan interface{}) { |
||||
b.unreg <- listener |
||||
} |
||||
|
||||
func (b *Broadcast) Close() { |
||||
if b.stopStatus == false { |
||||
b.stop <- 1 |
||||
} |
||||
} |
@ -0,0 +1,63 @@
|
||||
package broadcast |
||||
|
||||
import ( |
||||
"sync" |
||||
"testing" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
totalNum int = 5 |
||||
succNum int = 0 |
||||
mutex sync.Mutex |
||||
) |
||||
|
||||
func TestBroadcast(t *testing.T) { |
||||
b := NewBroadcast() |
||||
if b == nil { |
||||
t.Errorf("New Broadcast error, nil return") |
||||
} |
||||
defer b.Close() |
||||
|
||||
var wait sync.WaitGroup |
||||
wait.Add(totalNum) |
||||
for i := 0; i < totalNum; i++ { |
||||
go worker(b, &wait) |
||||
} |
||||
|
||||
time.Sleep(1e6 * 20) |
||||
msg := "test" |
||||
b.In() <- msg |
||||
|
||||
wait.Wait() |
||||
if succNum != totalNum { |
||||
t.Errorf("TotalNum %d, FailNum(timeout) %d", totalNum, totalNum-succNum) |
||||
} |
||||
} |
||||
|
||||
func worker(b *Broadcast, wait *sync.WaitGroup) { |
||||
defer wait.Done() |
||||
msgChan := b.Reg() |
||||
|
||||
// exit if nothing got in 2 seconds
|
||||
timeout := make(chan bool, 1) |
||||
go func() { |
||||
time.Sleep(time.Duration(2) * time.Second) |
||||
timeout <- true |
||||
}() |
||||
|
||||
select { |
||||
case item := <-msgChan: |
||||
msg := item.(string) |
||||
if msg == "test" { |
||||
mutex.Lock() |
||||
succNum++ |
||||
mutex.Unlock() |
||||
} else { |
||||
break |
||||
} |
||||
|
||||
case <-timeout: |
||||
break |
||||
} |
||||
} |
Loading…
Reference in new issue