nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/protocol/xpull/xpull.go (about) 1 // Copyright 2019 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 xpull implements the PULL protocol. This read only protocol 16 // simply receives messages from pipes. 17 package xpull 18 19 import ( 20 "sync" 21 "time" 22 23 "nanomsg.org/go/mangos/v2/protocol" 24 ) 25 26 // Protocol identity information. 27 const ( 28 Self = protocol.ProtoPull 29 Peer = protocol.ProtoPush 30 SelfName = "pull" 31 PeerName = "push" 32 ) 33 34 type pipe struct { 35 p protocol.Pipe 36 s *socket 37 closeQ chan struct{} 38 } 39 40 type socket struct { 41 closed bool 42 closeQ chan struct{} 43 sizeQ chan struct{} 44 recvQ chan *protocol.Message 45 recvQLen int 46 resizeDiscards bool // only for testing (facilitates coverage) 47 recvExpire time.Duration 48 sync.Mutex 49 } 50 51 var ( 52 nilQ <-chan time.Time 53 ) 54 55 const defaultQLen = 128 56 57 func (s *socket) SendMsg(m *protocol.Message) error { 58 return protocol.ErrProtoOp 59 } 60 61 func (s *socket) RecvMsg() (*protocol.Message, error) { 62 // For now this uses a simple unified queue for the entire 63 // socket. Later we can look at moving this to priority queues 64 // based on socket pipes. 65 tq := nilQ 66 for { 67 s.Lock() 68 if s.recvExpire > 0 { 69 tq = time.After(s.recvExpire) 70 } 71 cq := s.closeQ 72 rq := s.recvQ 73 zq := s.sizeQ 74 s.Unlock() 75 select { 76 case <-cq: 77 return nil, protocol.ErrClosed 78 case <-tq: 79 return nil, protocol.ErrRecvTimeout 80 case <-zq: 81 continue 82 case m := <-rq: 83 return m, nil 84 } 85 } 86 } 87 88 func (s *socket) SetOption(name string, value interface{}) error { 89 switch name { 90 91 case protocol.OptionRecvDeadline: 92 if v, ok := value.(time.Duration); ok { 93 s.Lock() 94 s.recvExpire = v 95 s.Unlock() 96 return nil 97 } 98 return protocol.ErrBadValue 99 100 case protocol.OptionReadQLen: 101 if v, ok := value.(int); ok && v >= 0 { 102 newQ := make(chan *protocol.Message, v) 103 s.Lock() 104 s.recvQLen = v 105 oldQ := s.recvQ 106 s.recvQ = newQ 107 zq := s.sizeQ 108 s.sizeQ = make(chan struct{}) 109 discard := s.resizeDiscards 110 s.Unlock() 111 112 close(zq) 113 if !discard { 114 for { 115 var m *protocol.Message 116 select { 117 case m = <-oldQ: 118 default: 119 } 120 if m == nil { 121 break 122 } 123 select { 124 case newQ <- m: 125 default: 126 m.Free() 127 } 128 } 129 } 130 return nil 131 } 132 return protocol.ErrBadValue 133 134 case "_resizeDiscards": 135 // This option is here to facilitate testing. 136 if v, ok := value.(bool); ok { 137 s.Lock() 138 s.resizeDiscards = v 139 s.Unlock() 140 } 141 return nil 142 } 143 144 return protocol.ErrBadOption 145 } 146 147 func (s *socket) GetOption(option string) (interface{}, error) { 148 switch option { 149 case protocol.OptionRaw: 150 return true, nil 151 case protocol.OptionRecvDeadline: 152 s.Lock() 153 v := s.recvExpire 154 s.Unlock() 155 return v, nil 156 case protocol.OptionReadQLen: 157 s.Lock() 158 v := s.recvQLen 159 s.Unlock() 160 return v, nil 161 } 162 163 return nil, protocol.ErrBadOption 164 } 165 166 func (s *socket) AddPipe(pp protocol.Pipe) error { 167 p := &pipe{ 168 p: pp, 169 s: s, 170 closeQ: make(chan struct{}), 171 } 172 pp.SetPrivate(p) 173 s.Lock() 174 defer s.Unlock() 175 if s.closed { 176 return protocol.ErrClosed 177 } 178 go p.receiver() 179 return nil 180 } 181 182 func (s *socket) RemovePipe(pp protocol.Pipe) { 183 p := pp.GetPrivate().(*pipe) 184 close(p.closeQ) 185 } 186 187 func (s *socket) OpenContext() (protocol.Context, error) { 188 return nil, protocol.ErrProtoOp 189 } 190 191 func (*socket) Info() protocol.Info { 192 return protocol.Info{ 193 Self: Self, 194 Peer: Peer, 195 SelfName: SelfName, 196 PeerName: PeerName, 197 } 198 } 199 200 func (s *socket) Close() error { 201 s.Lock() 202 if s.closed { 203 s.Unlock() 204 return protocol.ErrClosed 205 } 206 s.closed = true 207 s.Unlock() 208 close(s.closeQ) 209 return nil 210 } 211 212 func (p *pipe) receiver() { 213 s := p.s 214 outer: 215 for { 216 m := p.p.RecvMsg() 217 if m == nil { 218 break 219 } 220 221 inner: 222 for { 223 s.Lock() 224 rq := s.recvQ 225 zq := s.sizeQ 226 s.Unlock() 227 228 select { 229 case rq <- m: 230 continue outer 231 case <-zq: 232 continue inner 233 case <-p.closeQ: 234 m.Free() 235 break outer 236 } 237 } 238 } 239 p.close() 240 } 241 242 func (p *pipe) close() { 243 _ = p.p.Close() 244 } 245 246 // NewProtocol returns a new protocol implementation. 247 func NewProtocol() protocol.Protocol { 248 s := &socket{ 249 closeQ: make(chan struct{}), 250 sizeQ: make(chan struct{}), 251 recvQ: make(chan *protocol.Message, defaultQLen), 252 recvQLen: defaultQLen, 253 } 254 return s 255 } 256 257 // NewSocket allocates a raw Socket using the PULL protocol. 258 func NewSocket() (protocol.Socket, error) { 259 return protocol.MakeSocket(NewProtocol()), nil 260 }