github.com/gdamore/mangos@v1.4.0/transport/inproc/inproc.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 inproc implements an simple inproc transport for mangos. 16 package inproc 17 18 import ( 19 "strings" 20 "sync" 21 22 "nanomsg.org/go-mangos" 23 ) 24 25 // inproc implements the Pipe interface on top of channels. 26 type inproc struct { 27 rq chan *mangos.Message 28 wq chan *mangos.Message 29 closeq chan struct{} 30 readyq chan struct{} 31 proto mangos.Protocol 32 addr addr 33 peer *inproc 34 sync.Mutex 35 } 36 37 type addr string 38 39 func (a addr) String() string { 40 s := string(a) 41 if strings.HasPrefix(s, "inproc://") { 42 s = s[len("inproc://"):] 43 } 44 return s 45 } 46 47 func (addr) Network() string { 48 return "inproc" 49 } 50 51 type listener struct { 52 addr string 53 proto mangos.Protocol 54 accepters []*inproc 55 } 56 57 type inprocTran struct{} 58 59 var listeners struct { 60 // Who is listening, on which "address"? 61 byAddr map[string]*listener 62 cv sync.Cond 63 mx sync.Mutex 64 } 65 66 func init() { 67 listeners.byAddr = make(map[string]*listener) 68 listeners.cv.L = &listeners.mx 69 } 70 71 func (p *inproc) Recv() (*mangos.Message, error) { 72 73 if p.peer == nil { 74 return nil, mangos.ErrClosed 75 } 76 select { 77 case m, ok := <-p.rq: 78 if m == nil || !ok { 79 return nil, mangos.ErrClosed 80 } 81 // Upper protocols expect to have to pick header and 82 // body part. So mush them back together. 83 //msg.Body = append(msg.Header, msg.Body...) 84 //msg.Header = make([]byte, 0, 32) 85 return m, nil 86 case <-p.closeq: 87 return nil, mangos.ErrClosed 88 case <-p.peer.closeq: 89 return nil, mangos.ErrClosed 90 } 91 } 92 93 func (p *inproc) Send(m *mangos.Message) error { 94 95 if p.peer == nil { 96 return mangos.ErrClosed 97 } 98 99 if m.Expired() { 100 m.Free() 101 return nil 102 } 103 104 // Upper protocols expect to have to pick header and body part. 105 // Also we need to have a fresh copy of the message for receiver, to 106 // break ownership. 107 nmsg := mangos.NewMessage(len(m.Header) + len(m.Body)) 108 nmsg.Body = append(nmsg.Body, m.Header...) 109 nmsg.Body = append(nmsg.Body, m.Body...) 110 select { 111 case p.wq <- nmsg: 112 return nil 113 case <-p.closeq: 114 nmsg.Free() 115 return mangos.ErrClosed 116 case <-p.peer.closeq: 117 nmsg.Free() 118 return mangos.ErrClosed 119 } 120 } 121 122 func (p *inproc) LocalProtocol() uint16 { 123 return p.proto.Number() 124 } 125 126 func (p *inproc) RemoteProtocol() uint16 { 127 return p.proto.PeerNumber() 128 } 129 130 func (p *inproc) Close() error { 131 p.Lock() 132 defer p.Unlock() 133 if p.IsOpen() { 134 close(p.closeq) 135 } 136 return nil 137 } 138 139 func (p *inproc) IsOpen() bool { 140 select { 141 case <-p.closeq: 142 return false 143 default: 144 return true 145 } 146 } 147 148 func (p *inproc) GetProp(name string) (interface{}, error) { 149 switch name { 150 case mangos.PropRemoteAddr: 151 return p.addr, nil 152 case mangos.PropLocalAddr: 153 return p.addr, nil 154 } 155 // We have no special properties 156 return nil, mangos.ErrBadProperty 157 } 158 159 type dialer struct { 160 addr string 161 proto mangos.Protocol 162 } 163 164 func (d *dialer) Dial() (mangos.Pipe, error) { 165 166 var server *inproc 167 client := &inproc{proto: d.proto, addr: addr(d.addr)} 168 client.readyq = make(chan struct{}) 169 client.closeq = make(chan struct{}) 170 171 listeners.mx.Lock() 172 173 // NB: No timeouts here! 174 for { 175 var l *listener 176 var ok bool 177 if l, ok = listeners.byAddr[d.addr]; !ok || l == nil { 178 listeners.mx.Unlock() 179 return nil, mangos.ErrConnRefused 180 } 181 182 if !mangos.ValidPeers(client.proto, l.proto) { 183 return nil, mangos.ErrBadProto 184 } 185 186 if len(l.accepters) != 0 { 187 server = l.accepters[len(l.accepters)-1] 188 l.accepters = l.accepters[:len(l.accepters)-1] 189 break 190 } 191 192 listeners.cv.Wait() 193 continue 194 } 195 196 listeners.mx.Unlock() 197 198 server.wq = make(chan *mangos.Message) 199 server.rq = make(chan *mangos.Message) 200 client.rq = server.wq 201 client.wq = server.rq 202 server.peer = client 203 client.peer = server 204 205 close(server.readyq) 206 close(client.readyq) 207 return client, nil 208 } 209 210 func (*dialer) SetOption(string, interface{}) error { 211 return mangos.ErrBadOption 212 } 213 214 func (*dialer) GetOption(string) (interface{}, error) { 215 return nil, mangos.ErrBadOption 216 } 217 218 func (l *listener) Listen() error { 219 listeners.mx.Lock() 220 if _, ok := listeners.byAddr[l.addr]; ok { 221 listeners.mx.Unlock() 222 return mangos.ErrAddrInUse 223 } 224 listeners.byAddr[l.addr] = l 225 listeners.cv.Broadcast() 226 listeners.mx.Unlock() 227 return nil 228 } 229 230 func (l *listener) Address() string { 231 return l.addr 232 } 233 234 func (l *listener) Accept() (mangos.Pipe, error) { 235 server := &inproc{proto: l.proto, addr: addr(l.addr)} 236 server.readyq = make(chan struct{}) 237 server.closeq = make(chan struct{}) 238 239 listeners.mx.Lock() 240 l.accepters = append(l.accepters, server) 241 listeners.cv.Broadcast() 242 listeners.mx.Unlock() 243 244 select { 245 case <-server.readyq: 246 return server, nil 247 case <-server.closeq: 248 return nil, mangos.ErrClosed 249 } 250 } 251 252 func (*listener) SetOption(string, interface{}) error { 253 return mangos.ErrBadOption 254 } 255 256 func (*listener) GetOption(string) (interface{}, error) { 257 return nil, mangos.ErrBadOption 258 } 259 260 func (l *listener) Close() error { 261 listeners.mx.Lock() 262 if listeners.byAddr[l.addr] == l { 263 delete(listeners.byAddr, l.addr) 264 } 265 servers := l.accepters 266 l.accepters = nil 267 listeners.cv.Broadcast() 268 listeners.mx.Unlock() 269 270 for _, s := range servers { 271 close(s.closeq) 272 } 273 274 return nil 275 } 276 277 func (t *inprocTran) Scheme() string { 278 return "inproc" 279 } 280 281 func (t *inprocTran) NewDialer(addr string, sock mangos.Socket) (mangos.PipeDialer, error) { 282 if _, err := mangos.StripScheme(t, addr); err != nil { 283 return nil, err 284 } 285 return &dialer{addr: addr, proto: sock.GetProtocol()}, nil 286 } 287 288 func (t *inprocTran) NewListener(addr string, sock mangos.Socket) (mangos.PipeListener, error) { 289 if _, err := mangos.StripScheme(t, addr); err != nil { 290 return nil, err 291 } 292 l := &listener{addr: addr, proto: sock.GetProtocol()} 293 return l, nil 294 } 295 296 // NewTransport allocates a new inproc:// transport. 297 func NewTransport() mangos.Transport { 298 return &inprocTran{} 299 }