github.com/gdamore/mangos@v1.4.0/protocol/req/req.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 req implements the REQ protocol, which is the request side of 16 // the request/response pattern. (REP is the response.) 17 package req 18 19 import ( 20 "encoding/binary" 21 "sync" 22 "time" 23 24 "nanomsg.org/go-mangos" 25 ) 26 27 // req is an implementation of the req protocol. 28 type req struct { 29 sync.Mutex 30 sock mangos.ProtocolSocket 31 eps map[uint32]*reqEp 32 resend chan *mangos.Message 33 raw bool 34 retry time.Duration 35 nextid uint32 36 waker *time.Timer 37 w mangos.Waiter 38 init sync.Once 39 40 // fields describing the outstanding request 41 reqmsg *mangos.Message 42 reqid uint32 43 } 44 45 type reqEp struct { 46 ep mangos.Endpoint 47 cq chan struct{} 48 } 49 50 func (r *req) Init(socket mangos.ProtocolSocket) { 51 r.sock = socket 52 r.eps = make(map[uint32]*reqEp) 53 r.resend = make(chan *mangos.Message) 54 r.w.Init() 55 56 r.nextid = uint32(time.Now().UnixNano()) // quasi-random 57 r.retry = time.Minute * 1 // retry after a minute 58 r.waker = time.NewTimer(r.retry) 59 r.waker.Stop() 60 r.sock.SetRecvError(mangos.ErrProtoState) 61 } 62 63 func (r *req) Shutdown(expire time.Time) { 64 r.w.WaitAbsTimeout(expire) 65 } 66 67 // nextID returns the next request ID. 68 func (r *req) nextID() uint32 { 69 // The high order bit is "special", and must always be set. (This is 70 // how the peer will detect the end of the backtrace.) 71 v := r.nextid | 0x80000000 72 r.nextid++ 73 return v 74 } 75 76 // resend sends the request message again, after a timer has expired. 77 func (r *req) resender() { 78 79 defer r.w.Done() 80 cq := r.sock.CloseChannel() 81 82 for { 83 select { 84 case <-r.waker.C: 85 case <-cq: 86 return 87 } 88 89 r.Lock() 90 m := r.reqmsg 91 if m == nil { 92 r.Unlock() 93 continue 94 } 95 m = m.Dup() 96 r.Unlock() 97 98 r.resend <- m 99 r.Lock() 100 if r.retry > 0 { 101 r.waker.Reset(r.retry) 102 } else { 103 r.waker.Stop() 104 } 105 r.Unlock() 106 } 107 } 108 109 func (r *req) receiver(ep mangos.Endpoint) { 110 rq := r.sock.RecvChannel() 111 cq := r.sock.CloseChannel() 112 113 for { 114 m := ep.RecvMsg() 115 if m == nil { 116 break 117 } 118 119 if len(m.Body) < 4 { 120 m.Free() 121 continue 122 } 123 m.Header = append(m.Header, m.Body[:4]...) 124 m.Body = m.Body[4:] 125 126 select { 127 case rq <- m: 128 case <-cq: 129 m.Free() 130 break 131 } 132 } 133 } 134 135 func (r *req) sender(pe *reqEp) { 136 137 // NB: Because this function is only called when an endpoint is 138 // added, we can reasonably safely cache the channels -- they won't 139 // be changing after this point. 140 141 defer r.w.Done() 142 sq := r.sock.SendChannel() 143 cq := r.sock.CloseChannel() 144 rq := r.resend 145 146 for { 147 var m *mangos.Message 148 149 select { 150 case m = <-rq: 151 case m = <-sq: 152 case <-cq: 153 return 154 case <-pe.cq: 155 return 156 } 157 158 if pe.ep.SendMsg(m) != nil { 159 r.resend <- m 160 break 161 } 162 } 163 } 164 165 func (*req) Number() uint16 { 166 return mangos.ProtoReq 167 } 168 169 func (*req) PeerNumber() uint16 { 170 return mangos.ProtoRep 171 } 172 173 func (*req) Name() string { 174 return "req" 175 } 176 177 func (*req) PeerName() string { 178 return "rep" 179 } 180 181 func (r *req) AddEndpoint(ep mangos.Endpoint) { 182 183 r.init.Do(func() { 184 r.w.Add() 185 go r.resender() 186 }) 187 188 pe := &reqEp{cq: make(chan struct{}), ep: ep} 189 r.Lock() 190 r.eps[ep.GetID()] = pe 191 192 r.Unlock() 193 go r.receiver(ep) 194 r.w.Add() 195 go r.sender(pe) 196 } 197 198 func (r *req) RemoveEndpoint(ep mangos.Endpoint) { 199 id := ep.GetID() 200 r.Lock() 201 pe := r.eps[id] 202 delete(r.eps, id) 203 r.Unlock() 204 if pe != nil { 205 close(pe.cq) 206 } 207 } 208 209 func (r *req) SendHook(m *mangos.Message) bool { 210 211 if r.raw { 212 // Raw mode has no automatic retry, and must include the 213 // request id in the header coming down. 214 return true 215 } 216 r.Lock() 217 defer r.Unlock() 218 219 // We need to generate a new request id, and append it to the header. 220 r.reqid = r.nextID() 221 v := r.reqid 222 m.Header = append(m.Header, 223 byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 224 225 r.reqmsg = m.Dup() 226 227 // Schedule a retry, in case we don't get a reply. 228 if r.retry > 0 { 229 r.waker.Reset(r.retry) 230 } else { 231 r.waker.Stop() 232 } 233 234 r.sock.SetRecvError(nil) 235 236 return true 237 } 238 239 func (r *req) RecvHook(m *mangos.Message) bool { 240 if r.raw { 241 // Raw mode just passes up messages unmolested. 242 return true 243 } 244 r.Lock() 245 defer r.Unlock() 246 if len(m.Header) < 4 { 247 return false 248 } 249 if r.reqmsg == nil { 250 return false 251 } 252 if binary.BigEndian.Uint32(m.Header) != r.reqid { 253 return false 254 } 255 r.waker.Stop() 256 m = r.reqmsg 257 r.reqmsg = nil 258 m.Free() 259 r.sock.SetRecvError(mangos.ErrProtoState) 260 return true 261 } 262 263 func (r *req) SetOption(option string, value interface{}) error { 264 var ok bool 265 switch option { 266 case mangos.OptionRaw: 267 if r.raw, ok = value.(bool); !ok { 268 return mangos.ErrBadValue 269 } 270 if r.raw { 271 r.sock.SetRecvError(nil) 272 } else { 273 r.sock.SetRecvError(mangos.ErrProtoState) 274 } 275 return nil 276 case mangos.OptionRetryTime: 277 r.Lock() 278 r.retry, ok = value.(time.Duration) 279 r.Unlock() 280 if !ok { 281 return mangos.ErrBadValue 282 } 283 return nil 284 default: 285 return mangos.ErrBadOption 286 } 287 } 288 289 func (r *req) GetOption(option string) (interface{}, error) { 290 switch option { 291 case mangos.OptionRaw: 292 return r.raw, nil 293 case mangos.OptionRetryTime: 294 r.Lock() 295 v := r.retry 296 r.Unlock() 297 return v, nil 298 default: 299 return nil, mangos.ErrBadOption 300 } 301 } 302 303 // NewSocket allocates a new Socket using the REQ protocol. 304 func NewSocket() (mangos.Socket, error) { 305 return mangos.MakeSocket(&req{}), nil 306 }