nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/internal/core/pipe.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 core 16 17 import ( 18 "crypto/rand" 19 "encoding/binary" 20 "nanomsg.org/go/mangos/v2" 21 "nanomsg.org/go/mangos/v2/transport" 22 "sync" 23 ) 24 25 // This is an application-wide global ID allocator. Unfortunately we need 26 // to have unique pipe IDs globally to permit certain things to work 27 // correctly. 28 29 type pipeIDAllocator struct { 30 used map[uint32]struct{} 31 next uint32 32 lock sync.Mutex 33 } 34 35 func (p *pipeIDAllocator) Get() uint32 { 36 p.lock.Lock() 37 defer p.lock.Unlock() 38 if p.used == nil { 39 b := make([]byte, 4) 40 // The following could in theory fail, but in that case 41 // we will wind up with IDs starting at zero. It should 42 // not happen unless the platform can't get good entropy. 43 _, _ = rand.Read(b) 44 p.used = make(map[uint32]struct{}) 45 p.next = binary.BigEndian.Uint32(b) 46 } 47 for { 48 id := p.next & 0x7fffffff 49 p.next++ 50 if id == 0 { 51 continue 52 } 53 if _, ok := p.used[id]; ok { 54 continue 55 } 56 p.used[id] = struct{}{} 57 return id 58 } 59 } 60 61 func (p *pipeIDAllocator) Free(id uint32) { 62 p.lock.Lock() 63 if _, ok := p.used[id]; !ok { 64 panic("free of unused pipe ID") 65 } 66 delete(p.used, id) 67 p.lock.Unlock() 68 } 69 70 var pipeIDs pipeIDAllocator 71 72 type pipeList struct { 73 pipes map[uint32]*pipe 74 lock sync.Mutex 75 } 76 77 func (l *pipeList) Add(p *pipe) { 78 l.lock.Lock() 79 if l.pipes == nil { 80 l.pipes = make(map[uint32]*pipe) 81 } 82 l.pipes[p.id] = p 83 l.lock.Unlock() 84 } 85 86 func (l *pipeList) Remove(p *pipe) { 87 l.lock.Lock() 88 delete(l.pipes, p.id) 89 l.lock.Unlock() 90 } 91 92 // CloseAll closes all pipes, asynchronously. 93 func (l *pipeList) CloseAll() { 94 l.lock.Lock() 95 for _, p := range l.pipes { 96 go p.close() 97 } 98 l.lock.Unlock() 99 } 100 101 // pipe wraps the Pipe data structure with the stuff we need to keep 102 // for the core. It implements the Pipe interface. 103 type pipe struct { 104 id uint32 105 p transport.Pipe 106 l *listener 107 d *dialer 108 s *socket 109 closeOnce sync.Once 110 data interface{} // Protocol private 111 added bool 112 closing bool 113 lock sync.Mutex // held across calls to remPipe and addPipe 114 } 115 116 func newPipe(tp transport.Pipe, s *socket, d *dialer, l *listener) *pipe { 117 p := &pipe{ 118 p: tp, 119 d: d, 120 l: l, 121 s: s, 122 id: pipeIDs.Get(), 123 } 124 return p 125 } 126 127 func (p *pipe) ID() uint32 { 128 return p.id 129 } 130 131 func (p *pipe) close() { 132 _ = p.Close() 133 } 134 135 func (p *pipe) Close() error { 136 p.closeOnce.Do(func() { 137 // Close the underlying transport pipe first. 138 _ = p.p.Close() 139 140 // Deregister it from the socket. This will also arrange 141 // for asynchronously running the event callback, and 142 // releasing the pipe ID for reuse. 143 p.lock.Lock() 144 p.closing = true 145 if p.added { 146 p.s.remPipe(p) 147 } 148 p.lock.Unlock() 149 150 if p.d != nil { 151 // Inform the dialer so that it will redial. 152 go p.d.pipeClosed() 153 } 154 }) 155 return nil 156 } 157 158 func (p *pipe) SendMsg(msg *mangos.Message) error { 159 160 if err := p.p.Send(msg); err != nil { 161 _ = p.Close() 162 return err 163 } 164 return nil 165 } 166 167 func (p *pipe) RecvMsg() *mangos.Message { 168 169 msg, err := p.p.Recv() 170 if err != nil { 171 _ = p.Close() 172 return nil 173 } 174 msg.Pipe = p 175 return msg 176 } 177 178 func (p *pipe) Address() string { 179 switch { 180 case p.l != nil: 181 return p.l.Address() 182 case p.d != nil: 183 return p.d.Address() 184 } 185 return "" 186 } 187 188 func (p *pipe) GetOption(name string) (interface{}, error) { 189 val, err := p.p.GetOption(name) 190 if err == mangos.ErrBadOption { 191 if p.d != nil { 192 val, err = p.d.GetOption(name) 193 } else if p.l != nil { 194 val, err = p.l.GetOption(name) 195 } 196 } 197 return val, err 198 } 199 200 func (p *pipe) Dialer() mangos.Dialer { 201 if p.d == nil { 202 return nil 203 } 204 return p.d 205 } 206 207 func (p *pipe) Listener() mangos.Listener { 208 if p.l == nil { 209 return nil 210 } 211 return p.l 212 } 213 214 func (p *pipe) SetPrivate(i interface{}) { 215 p.data = i 216 } 217 218 func (p *pipe) GetPrivate() interface{} { 219 return p.data 220 }