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