github.com/gdamore/mangos@v1.4.0/protocol/pub/pub.go (about) 1 // Copyright 2018 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package pub implements the PUB protocol. This protocol publishes messages 16 // to subscribers (SUB peers). The subscribers will filter incoming messages 17 // from the publisher based on their subscription. 18 package pub 19 20 import ( 21 "sync" 22 "time" 23 24 "nanomsg.org/go-mangos" 25 ) 26 27 type pubEp struct { 28 ep mangos.Endpoint 29 q chan *mangos.Message 30 p *pub 31 w mangos.Waiter 32 } 33 34 type pub struct { 35 sock mangos.ProtocolSocket 36 eps map[uint32]*pubEp 37 raw bool 38 w mangos.Waiter 39 40 sync.Mutex 41 } 42 43 func (p *pub) Init(sock mangos.ProtocolSocket) { 44 p.sock = sock 45 p.eps = make(map[uint32]*pubEp) 46 p.sock.SetRecvError(mangos.ErrProtoOp) 47 p.w.Init() 48 p.w.Add() 49 go p.sender() 50 } 51 52 func (p *pub) Shutdown(expire time.Time) { 53 54 p.w.WaitAbsTimeout(expire) 55 56 p.Lock() 57 peers := p.eps 58 p.eps = make(map[uint32]*pubEp) 59 p.Unlock() 60 61 for id, peer := range peers { 62 mangos.DrainChannel(peer.q, expire) 63 close(peer.q) 64 delete(peers, id) 65 } 66 } 67 68 // Bottom sender. 69 func (pe *pubEp) peerSender() { 70 71 for { 72 m := <-pe.q 73 if m == nil { 74 break 75 } 76 77 if pe.ep.SendMsg(m) != nil { 78 m.Free() 79 break 80 } 81 } 82 } 83 84 // Top sender. 85 func (p *pub) sender() { 86 defer p.w.Done() 87 88 cq := p.sock.CloseChannel() 89 sq := p.sock.SendChannel() 90 91 for { 92 select { 93 case <-cq: 94 return 95 96 case m := <-sq: 97 if m == nil { 98 sq = p.sock.SendChannel() 99 continue 100 } 101 102 p.Lock() 103 for _, peer := range p.eps { 104 m := m.Dup() 105 select { 106 case peer.q <- m: 107 default: 108 m.Free() 109 } 110 } 111 p.Unlock() 112 m.Free() 113 } 114 } 115 } 116 117 func (p *pub) AddEndpoint(ep mangos.Endpoint) { 118 depth := 16 119 if i, err := p.sock.GetOption(mangos.OptionWriteQLen); err == nil { 120 depth = i.(int) 121 } 122 pe := &pubEp{ep: ep, p: p, q: make(chan *mangos.Message, depth)} 123 pe.w.Init() 124 p.Lock() 125 p.eps[ep.GetID()] = pe 126 p.Unlock() 127 128 pe.w.Add() 129 go pe.peerSender() 130 go mangos.NullRecv(ep) 131 } 132 133 func (p *pub) RemoveEndpoint(ep mangos.Endpoint) { 134 id := ep.GetID() 135 p.Lock() 136 pe := p.eps[id] 137 delete(p.eps, id) 138 p.Unlock() 139 if pe != nil { 140 close(pe.q) 141 } 142 } 143 144 func (*pub) Number() uint16 { 145 return mangos.ProtoPub 146 } 147 148 func (*pub) PeerNumber() uint16 { 149 return mangos.ProtoSub 150 } 151 152 func (*pub) Name() string { 153 return "pub" 154 } 155 156 func (*pub) PeerName() string { 157 return "sub" 158 } 159 160 func (p *pub) SetOption(name string, v interface{}) error { 161 var ok bool 162 switch name { 163 case mangos.OptionRaw: 164 if p.raw, ok = v.(bool); !ok { 165 return mangos.ErrBadValue 166 } 167 return nil 168 default: 169 return mangos.ErrBadOption 170 } 171 } 172 173 func (p *pub) GetOption(name string) (interface{}, error) { 174 switch name { 175 case mangos.OptionRaw: 176 return p.raw, nil 177 default: 178 return nil, mangos.ErrBadOption 179 } 180 } 181 182 // NewSocket allocates a new Socket using the PUB protocol. 183 func NewSocket() (mangos.Socket, error) { 184 return mangos.MakeSocket(&pub{}), nil 185 }