1- // Copyright 2020 The Mangos Authors
1+ // Copyright 2021 The Mangos Authors
22//
33// Licensed under the Apache License, Version 2.0 (the "License");
44// you may not use file except in compliance with the License.
@@ -40,23 +40,24 @@ type pipe struct {
4040}
4141
4242type context struct {
43- s * socket
44- cond * sync.Cond
45- resendTime time.Duration // tunable resend time
46- sendExpire time.Duration // how long to wait in send
47- recvExpire time.Duration // how long to wait in recv
48- sendTimer * time.Timer // send timer
49- recvTimer * time.Timer // recv timer
50- resender * time.Timer // resend timeout
51- reqMsg * protocol.Message // message for transmit
52- repMsg * protocol.Message // received reply
53- sendMsg * protocol.Message // messaging waiting for send
54- lastPipe * pipe // last pipe used for transmit
55- reqID uint32 // request ID
56- recvWait bool // true if a thread is blocked in RecvMsg
57- bestEffort bool // if true, don't block waiting in send
58- queued bool // true if we need to send a message
59- closed bool // true if we are closed
43+ s * socket
44+ cond * sync.Cond
45+ resendTime time.Duration // tunable resend time
46+ sendExpire time.Duration // how long to wait in send
47+ recvExpire time.Duration // how long to wait in recv
48+ sendTimer * time.Timer // send timer
49+ recvTimer * time.Timer // recv timer
50+ resender * time.Timer // resend timeout
51+ reqMsg * protocol.Message // message for transmit
52+ repMsg * protocol.Message // received reply
53+ sendMsg * protocol.Message // messaging waiting for send
54+ lastPipe * pipe // last pipe used for transmit
55+ reqID uint32 // request ID
56+ recvWait bool // true if a thread is blocked in RecvMsg
57+ bestEffort bool // if true, don't block waiting in send
58+ failNoPeers bool // fast fail if no peers present
59+ queued bool // true if we need to send a message
60+ closed bool // true if we are closed
6061}
6162
6263type socket struct {
@@ -68,6 +69,7 @@ type socket struct {
6869 closed bool // true if we are closed
6970 sendq []* context // contexts waiting to send
7071 readyq []* pipe // pipes available for sending
72+ pipes map [uint32 ]* pipe // all pipes
7173}
7274
7375func (s * socket ) send () {
@@ -101,7 +103,7 @@ func (s *socket) send() {
101103 }
102104}
103105
104- func (p * pipe ) sendCtx (c * context , m * protocol.Message ) {
106+ func (p * pipe ) sendCtx (_ * context , m * protocol.Message ) {
105107 s := p .s
106108
107109 // Send this message. If an error occurs, we examine the
@@ -245,6 +247,9 @@ func (c *context) SendMsg(m *protocol.Message) error {
245247 return protocol .ErrClosed
246248 }
247249
250+ if c .failNoPeers && len (s .pipes ) == 0 {
251+ return protocol .ErrNoPeers
252+ }
248253 c .cancel () // this cancels any pending send or recv calls
249254 c .unscheduleSend ()
250255
@@ -281,7 +286,7 @@ func (c *context) SendMsg(m *protocol.Message) error {
281286 // It is responsible for providing the blocking semantic and
282287 // ultimately back-pressure. Note that we will "continue" if
283288 // the send is canceled by a subsequent send.
284- for c .sendMsg == m && ! expired && ! c .closed {
289+ for c .sendMsg == m && ! expired && ! c .closed && ! ( c . failNoPeers && len ( s . pipes ) == 0 ) {
285290 c .cond .Wait ()
286291 }
287292 if c .sendMsg == m {
@@ -291,6 +296,9 @@ func (c *context) SendMsg(m *protocol.Message) error {
291296 if c .closed {
292297 return protocol .ErrClosed
293298 }
299+ if c .failNoPeers && len (s .pipes ) == 0 {
300+ return protocol .ErrNoPeers
301+ }
294302 return protocol .ErrSendTimeout
295303 }
296304 return nil
@@ -303,6 +311,9 @@ func (c *context) RecvMsg() (*protocol.Message, error) {
303311 if s .closed || c .closed {
304312 return nil , protocol .ErrClosed
305313 }
314+ if c .failNoPeers && len (s .pipes ) == 0 {
315+ return nil , protocol .ErrNoPeers
316+ }
306317 if c .recvWait || c .reqID == 0 {
307318 return nil , protocol .ErrProtoState
308319 }
@@ -332,11 +343,14 @@ func (c *context) RecvMsg() (*protocol.Message, error) {
332343 c .cond .Broadcast ()
333344
334345 if m == nil {
346+ if c .closed {
347+ return nil , protocol .ErrClosed
348+ }
335349 if expired {
336350 return nil , protocol .ErrRecvTimeout
337351 }
338- if c .closed {
339- return nil , protocol .ErrClosed
352+ if c .failNoPeers && len ( s . pipes ) == 0 {
353+ return nil , protocol .ErrNoPeers
340354 }
341355 return nil , protocol .ErrCanceled
342356 }
@@ -380,6 +394,16 @@ func (c *context) SetOption(name string, value interface{}) error {
380394 return nil
381395 }
382396 return protocol .ErrBadValue
397+
398+ case protocol .OptionFailNoPeers :
399+ if v , ok := value .(bool ); ok {
400+ c .s .Lock ()
401+ c .failNoPeers = v
402+ c .s .Unlock ()
403+ return nil
404+ }
405+ return protocol .ErrBadValue
406+
383407 }
384408
385409 return protocol .ErrBadOption
@@ -407,6 +431,11 @@ func (c *context) GetOption(option string) (interface{}, error) {
407431 v := c .bestEffort
408432 c .s .Unlock ()
409433 return v , nil
434+ case protocol .OptionFailNoPeers :
435+ c .s .Lock ()
436+ v := c .failNoPeers
437+ c .s .Unlock ()
438+ return v , nil
410439 }
411440
412441 return nil , protocol .ErrBadOption
@@ -493,6 +522,7 @@ func (s *socket) AddPipe(pp protocol.Pipe) error {
493522 }
494523 s .readyq = append (s .readyq , p )
495524 s .send ()
525+ s .pipes [pp .ID ()] = p
496526 go p .receiver ()
497527 return nil
498528}
@@ -506,8 +536,11 @@ func (s *socket) RemovePipe(pp protocol.Pipe) {
506536 s .readyq = append (s .readyq [:i ], s .readyq [i + 1 :]... )
507537 }
508538 }
539+ delete (s .pipes , pp .ID ())
509540 for c := range s .ctxs {
510- if c .lastPipe == p && c .reqMsg != nil {
541+ if c .failNoPeers && len (s .pipes ) == 0 {
542+ c .cancel ()
543+ } else if c .lastPipe == p && c .reqMsg != nil {
511544 // We are closing this pipe, so we need to
512545 // immediately reschedule it.
513546 c .lastPipe = nil
@@ -539,6 +572,7 @@ func NewProtocol() protocol.Protocol {
539572 nextID : uint32 (time .Now ().UnixNano ()), // quasi-random
540573 ctxs : make (map [* context ]struct {}),
541574 ctxByID : make (map [uint32 ]* context ),
575+ pipes : make (map [uint32 ]* pipe ),
542576 }
543577 s .defCtx = & context {
544578 s : s ,
0 commit comments