forked from funny/link
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbroadcast.go
More file actions
130 lines (110 loc) · 3.22 KB
/
Copy pathbroadcast.go
File metadata and controls
130 lines (110 loc) · 3.22 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package link
import (
"github.com/funny/sync"
"time"
)
// Broadcaster.
type Broadcaster struct {
protocol ProtocolState
fetcher func(func(*Session))
}
// Broadcast work.
type BroadcastWork struct {
Session *Session
AsyncWork
}
// Create a broadcaster.
func NewBroadcaster(protocol ProtocolState, fetcher func(func(*Session))) *Broadcaster {
return &Broadcaster{
protocol: protocol,
fetcher: fetcher,
}
}
// Broadcast to sessions. The message only encoded once
// so the performance is better than send message one by one.
func (b *Broadcaster) Broadcast(message Message, timeout time.Duration) ([]BroadcastWork, error) {
buffer := newOutBuffer()
b.protocol.PrepareOutBuffer(buffer, message.OutBufferSize())
if err := message.WriteOutBuffer(buffer); err != nil {
buffer.free()
return nil, err
}
buffer.isBroadcast = true
works := make([]BroadcastWork, 0, 10)
b.fetcher(func(session *Session) {
buffer.broadcastUse()
works = append(works, BroadcastWork{
session,
session.asyncSendBuffer(buffer, timeout),
})
})
return works, nil
}
// The channel type. Used to maintain a group of session.
// Normally used for broadcast classify purpose.
type Channel struct {
mutex sync.RWMutex
sessions map[uint64]channelSession
broadcaster *Broadcaster
// channel state
State interface{}
}
type channelSession struct {
*Session
KickCallback func()
}
// Create a channel instance.
func NewChannel(protocol Protocol, side ProtocolSide) *Channel {
channel := &Channel{
sessions: make(map[uint64]channelSession),
}
protocolState, _ := protocol.New(channel, side)
channel.broadcaster = NewBroadcaster(protocolState, channel.Fetch)
return channel
}
// Broadcast to channel. The message only encoded once
// so the performance is better than send message one by one.
func (channel *Channel) Broadcast(message Message, timeout time.Duration) ([]BroadcastWork, error) {
return channel.broadcaster.Broadcast(message, timeout)
}
// How mush sessions in this channel.
func (channel *Channel) Len() int {
channel.mutex.RLock()
defer channel.mutex.RUnlock()
return len(channel.sessions)
}
// Join the channel. The kickCallback will called when the session kick out from the channel.
func (channel *Channel) Join(session *Session, kickCallback func()) {
channel.mutex.Lock()
defer channel.mutex.Unlock()
session.AddCloseCallback(channel, func() {
channel.Exit(session)
})
channel.sessions[session.Id()] = channelSession{session, kickCallback}
}
// Exit the channel.
func (channel *Channel) Exit(session *Session) {
channel.mutex.Lock()
defer channel.mutex.Unlock()
session.RemoveCloseCallback(channel)
delete(channel.sessions, session.Id())
}
// Kick out a session from the channel.
func (channel *Channel) Kick(sessionId uint64) {
channel.mutex.Lock()
defer channel.mutex.Unlock()
if session, exists := channel.sessions[sessionId]; exists {
delete(channel.sessions, sessionId)
if session.KickCallback != nil {
session.KickCallback()
}
}
}
// Fetch the sessions. NOTE: Invoke Kick() or Exit() in fetch callback will dead lock.
func (channel *Channel) Fetch(callback func(*Session)) {
channel.mutex.RLock()
defer channel.mutex.RUnlock()
for _, sesssion := range channel.sessions {
callback(sesssion.Session)
}
}