-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmulticast.go
More file actions
100 lines (98 loc) · 2.48 KB
/
multicast.go
File metadata and controls
100 lines (98 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package rx
import "sync"
// Multicast returns both an Observer and and Observable. The returned Observer is
// used to send items into the Multicast. The returned Observable is used to subscribe
// to the Multicast. The Multicast multicasts items send through the Observer to every
// Subscriber of the Observable.
//
// size size of the item buffer, number of items kept to replay to a new Subscriber.
//
// Backpressure handling depends on the sign of the size argument. For positive size the
// multicast will block when one of the subscribers lets the buffer fill up. For negative
// size the multicast will drop items on the blocking subscriber, allowing the others to
// keep on receiving values. For hot observables dropping is preferred.
func Multicast[T any](size int) (Observer[T], Observable[T]) {
var multicast struct {
sync.Mutex
channels []chan any
err error
done bool
}
drop := (size < 0)
if size < 0 {
size = -size
}
observer := func(next T, err error, done bool) {
multicast.Lock()
defer multicast.Unlock()
if !multicast.done {
for _, ch := range multicast.channels {
switch {
case !done:
if drop {
select {
case ch <- next:
default:
// dropping
}
} else {
ch <- next
}
case err != nil:
if drop {
select {
case ch <- err:
default:
// dropping
}
} else {
ch <- err
}
close(ch)
default:
close(ch)
}
}
multicast.err = err
multicast.done = done
}
}
observable := func(observe Observer[T], scheduler Scheduler, subscriber Subscriber) {
addChannel := func() <-chan any {
multicast.Lock()
defer multicast.Unlock()
var ch chan any
if !multicast.done {
ch = make(chan any, size)
multicast.channels = append(multicast.channels, ch)
} else if multicast.err != nil {
ch = make(chan any, 1)
ch <- multicast.err
close(ch)
} else {
ch = make(chan any)
close(ch)
}
return ch
}
removeChannel := func(ch <-chan any) func() {
return func() {
multicast.Lock()
defer multicast.Unlock()
if !multicast.done && ch != nil {
channels := multicast.channels[:0]
for _, c := range multicast.channels {
if ch != c {
channels = append(channels, c)
}
}
multicast.channels = channels
}
}
}
ch := addChannel()
Recv(ch)(observe.AsObserver(), scheduler, subscriber)
subscriber.OnUnsubscribe(removeChannel(ch))
}
return observer, observable
}