go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/req/req.go (about)

     1  // Copyright 2022 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  	"sync/atomic"
    23  	"time"
    24  
    25  	"go.nanomsg.org/mangos/v3/protocol"
    26  )
    27  
    28  // Protocol identity information.
    29  const (
    30  	Self     = protocol.ProtoReq
    31  	Peer     = protocol.ProtoRep
    32  	SelfName = "req"
    33  	PeerName = "rep"
    34  )
    35  
    36  type pipe struct {
    37  	p      protocol.Pipe
    38  	s      *socket
    39  	closed bool
    40  }
    41  
    42  type 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  	receiveExpire time.Duration     // how long to wait in receive
    48  	sendTimer     *time.Timer       // send timer
    49  	receiveTimer  *time.Timer       // receive timer
    50  	resendTimer   *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  	receiveWait   bool              // true if a thread is blocked receiving
    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
    61  }
    62  
    63  type socket struct {
    64  	sync.Mutex
    65  	defCtx   *context              // default context
    66  	contexts map[*context]struct{} // all contexts (set)
    67  	ctxByID  map[uint32]*context   // contexts by request ID
    68  	nextID   uint32                // next request ID
    69  	closed   bool                  // true if we are closed
    70  	sendQ    []*context            // contexts waiting to send
    71  	readyQ   []*pipe               // pipes available for sending
    72  	pipes    map[uint32]*pipe      // all pipes
    73  }
    74  
    75  func (s *socket) send() {
    76  	for len(s.sendQ) != 0 && len(s.readyQ) != 0 {
    77  		c := s.sendQ[0]
    78  		s.sendQ = s.sendQ[1:]
    79  		c.queued = false
    80  
    81  		var m *protocol.Message
    82  		if m = c.sendMsg; m != nil {
    83  			c.reqMsg = m
    84  			c.sendMsg = nil
    85  			s.ctxByID[c.reqID] = c
    86  			c.cond.Broadcast()
    87  		} else {
    88  			m = c.reqMsg
    89  		}
    90  		m.Clone()
    91  		p := s.readyQ[0]
    92  		s.readyQ = s.readyQ[1:]
    93  
    94  		// Schedule retransmission for the future.
    95  		c.lastPipe = p
    96  		if c.resendTime > 0 {
    97  			id := c.reqID
    98  			c.resendTimer = time.AfterFunc(c.resendTime, func() {
    99  				c.resendMessage(id)
   100  			})
   101  		}
   102  		go p.sendCtx(c, m)
   103  	}
   104  }
   105  
   106  func (p *pipe) sendCtx(_ *context, m *protocol.Message) {
   107  	s := p.s
   108  
   109  	// Send this message.  If an error occurs, we examine the
   110  	// error.  If it is ErrClosed, we don't schedule our self.
   111  	if err := p.p.SendMsg(m); err != nil {
   112  		m.Free()
   113  		if err == protocol.ErrClosed {
   114  			return
   115  		}
   116  	}
   117  	s.Lock()
   118  	if !s.closed && !p.closed {
   119  		s.readyQ = append(s.readyQ, p)
   120  		s.send()
   121  	}
   122  	s.Unlock()
   123  }
   124  
   125  func (p *pipe) receiver() {
   126  	s := p.s
   127  	for {
   128  		m := p.p.RecvMsg()
   129  		if m == nil {
   130  			break
   131  		}
   132  		if len(m.Body) < 4 {
   133  			m.Free()
   134  			continue
   135  		}
   136  		m.Header = append(m.Header, m.Body[:4]...)
   137  		m.Body = m.Body[4:]
   138  
   139  		id := binary.BigEndian.Uint32(m.Header)
   140  
   141  		s.Lock()
   142  		// Since we just received a reply, stick our send at the
   143  		// head of the list, since that's a good indication that
   144  		// we're ready for another request.
   145  		for i, rp := range s.readyQ {
   146  			if p == rp {
   147  				s.readyQ[0], s.readyQ[i] = s.readyQ[i], s.readyQ[0]
   148  				break
   149  			}
   150  		}
   151  
   152  		if c, ok := s.ctxByID[id]; ok {
   153  			c.cancelSend()
   154  			c.reqMsg.Free()
   155  			c.reqMsg = nil
   156  			c.repMsg = m
   157  			delete(s.ctxByID, id)
   158  			if c.resendTimer != nil {
   159  				c.resendTimer.Stop()
   160  				c.resendTimer = nil
   161  			}
   162  			if c.receiveTimer != nil {
   163  				c.receiveTimer.Stop()
   164  				c.receiveTimer = nil
   165  			}
   166  			c.cond.Broadcast()
   167  		} else {
   168  			// No matching receiver so just drop it.
   169  			m.Free()
   170  		}
   171  		s.Unlock()
   172  	}
   173  
   174  	go p.Close()
   175  }
   176  
   177  func (p *pipe) Close() {
   178  	_ = p.p.Close()
   179  }
   180  
   181  func (c *context) resendMessage(id uint32) {
   182  	s := c.s
   183  	s.Lock()
   184  	defer s.Unlock()
   185  	if c.reqID == id && c.reqMsg != nil {
   186  		if !c.queued {
   187  			c.queued = true
   188  			s.sendQ = append(s.sendQ, c)
   189  			s.send()
   190  		}
   191  	}
   192  }
   193  
   194  func (c *context) cancelSend() {
   195  	s := c.s
   196  	if c.queued {
   197  		c.queued = false
   198  		for i, c2 := range s.sendQ {
   199  			if c2 == c {
   200  				s.sendQ = append(s.sendQ[:i], s.sendQ[i+1:]...)
   201  				return
   202  			}
   203  		}
   204  	}
   205  }
   206  
   207  func (c *context) cancel() {
   208  	s := c.s
   209  	c.cancelSend()
   210  	if c.reqID != 0 {
   211  		delete(s.ctxByID, c.reqID)
   212  		c.reqID = 0
   213  	}
   214  	if c.repMsg != nil {
   215  		c.repMsg.Free()
   216  		c.repMsg = nil
   217  	}
   218  	if c.reqMsg != nil {
   219  		c.reqMsg.Free()
   220  		c.reqMsg = nil
   221  	}
   222  	if c.resendTimer != nil {
   223  		c.resendTimer.Stop()
   224  		c.resendTimer = nil
   225  	}
   226  	if c.sendTimer != nil {
   227  		c.sendTimer.Stop()
   228  		c.sendTimer = nil
   229  	}
   230  	if c.receiveTimer != nil {
   231  		c.receiveTimer.Stop()
   232  		c.receiveTimer = nil
   233  	}
   234  	c.cond.Broadcast()
   235  }
   236  
   237  func (c *context) SendMsg(m *protocol.Message) error {
   238  
   239  	s := c.s
   240  
   241  	id := atomic.AddUint32(&s.nextID, 1)
   242  	id |= 0x80000000
   243  
   244  	// cooked mode, we stash the header
   245  	m.Header = append([]byte{},
   246  		byte(id>>24), byte(id>>16), byte(id>>8), byte(id))
   247  
   248  	s.Lock()
   249  	defer s.Unlock()
   250  	if s.closed || c.closed {
   251  		return protocol.ErrClosed
   252  	}
   253  
   254  	if c.failNoPeers && len(s.pipes) == 0 {
   255  		return protocol.ErrNoPeers
   256  	}
   257  	c.cancel() // this cancels any pending send or receive calls
   258  	c.cancelSend()
   259  
   260  	c.reqID = id
   261  	c.queued = true
   262  	c.sendMsg = m
   263  
   264  	s.sendQ = append(s.sendQ, c)
   265  
   266  	if c.bestEffort {
   267  		// for best effort case, we just immediately go the
   268  		// reqMsg, and schedule sending.  No waiting.
   269  		// This means that if the message cannot be delivered
   270  		// immediately, it will still get a chance later.
   271  		s.send()
   272  		return nil
   273  	}
   274  
   275  	expired := false
   276  	if c.sendExpire > 0 {
   277  		c.sendTimer = time.AfterFunc(c.sendExpire, func() {
   278  			s.Lock()
   279  			if c.sendMsg == m {
   280  				expired = true
   281  				c.cancel() // also does a wake-up
   282  			}
   283  			s.Unlock()
   284  		})
   285  	}
   286  
   287  	s.send()
   288  
   289  	// This sleeps until we are picked for scheduling.
   290  	// It is responsible for providing the blocking semantic and
   291  	// ultimately back-pressure.  Note that we will "continue" if
   292  	// sending is canceled by a subsequent send.
   293  	for c.sendMsg == m && !expired && !c.closed && !(c.failNoPeers && len(s.pipes) == 0) {
   294  		c.cond.Wait()
   295  	}
   296  	if c.sendMsg == m {
   297  		c.cancelSend()
   298  		c.sendMsg = nil
   299  		c.reqID = 0
   300  		if c.closed {
   301  			return protocol.ErrClosed
   302  		}
   303  		if c.failNoPeers && len(s.pipes) == 0 {
   304  			return protocol.ErrNoPeers
   305  		}
   306  		return protocol.ErrSendTimeout
   307  	}
   308  	return nil
   309  }
   310  
   311  func (c *context) RecvMsg() (*protocol.Message, error) {
   312  	s := c.s
   313  	s.Lock()
   314  	defer s.Unlock()
   315  	if s.closed || c.closed {
   316  		return nil, protocol.ErrClosed
   317  	}
   318  	if c.failNoPeers && len(s.pipes) == 0 {
   319  		return nil, protocol.ErrNoPeers
   320  	}
   321  	if c.receiveWait || c.reqID == 0 {
   322  		return nil, protocol.ErrProtoState
   323  	}
   324  	c.receiveWait = true
   325  	id := c.reqID
   326  	expired := false
   327  
   328  	if c.receiveExpire > 0 {
   329  		c.receiveTimer = time.AfterFunc(c.receiveExpire, func() {
   330  			s.Lock()
   331  			if c.reqID == id {
   332  				expired = true
   333  				c.cancel()
   334  			}
   335  			s.Unlock()
   336  		})
   337  	}
   338  
   339  	for id == c.reqID && c.repMsg == nil {
   340  		c.cond.Wait()
   341  	}
   342  
   343  	m := c.repMsg
   344  	c.reqID = 0
   345  	c.repMsg = nil
   346  	c.receiveWait = false
   347  	c.cond.Broadcast()
   348  
   349  	if m == nil {
   350  		if c.closed {
   351  			return nil, protocol.ErrClosed
   352  		}
   353  		if expired {
   354  			return nil, protocol.ErrRecvTimeout
   355  		}
   356  		if c.failNoPeers && len(s.pipes) == 0 {
   357  			return nil, protocol.ErrNoPeers
   358  		}
   359  		return nil, protocol.ErrCanceled
   360  	}
   361  	return m, nil
   362  }
   363  
   364  func (c *context) SetOption(name string, value interface{}) error {
   365  	switch name {
   366  	case protocol.OptionRetryTime:
   367  		if v, ok := value.(time.Duration); ok {
   368  			c.s.Lock()
   369  			c.resendTime = v
   370  			c.s.Unlock()
   371  			return nil
   372  		}
   373  		return protocol.ErrBadValue
   374  
   375  	case protocol.OptionRecvDeadline:
   376  		if v, ok := value.(time.Duration); ok {
   377  			c.s.Lock()
   378  			c.receiveExpire = v
   379  			c.s.Unlock()
   380  			return nil
   381  		}
   382  		return protocol.ErrBadValue
   383  
   384  	case protocol.OptionSendDeadline:
   385  		if v, ok := value.(time.Duration); ok {
   386  			c.s.Lock()
   387  			c.sendExpire = v
   388  			c.s.Unlock()
   389  			return nil
   390  		}
   391  		return protocol.ErrBadValue
   392  
   393  	case protocol.OptionBestEffort:
   394  		if v, ok := value.(bool); ok {
   395  			c.s.Lock()
   396  			c.bestEffort = v
   397  			c.s.Unlock()
   398  			return nil
   399  		}
   400  		return protocol.ErrBadValue
   401  
   402  	case protocol.OptionFailNoPeers:
   403  		if v, ok := value.(bool); ok {
   404  			c.s.Lock()
   405  			c.failNoPeers = v
   406  			c.s.Unlock()
   407  			return nil
   408  		}
   409  		return protocol.ErrBadValue
   410  
   411  	}
   412  
   413  	return protocol.ErrBadOption
   414  }
   415  
   416  func (c *context) GetOption(option string) (interface{}, error) {
   417  	switch option {
   418  	case protocol.OptionRetryTime:
   419  		c.s.Lock()
   420  		v := c.resendTime
   421  		c.s.Unlock()
   422  		return v, nil
   423  	case protocol.OptionRecvDeadline:
   424  		c.s.Lock()
   425  		v := c.receiveExpire
   426  		c.s.Unlock()
   427  		return v, nil
   428  	case protocol.OptionSendDeadline:
   429  		c.s.Lock()
   430  		v := c.sendExpire
   431  		c.s.Unlock()
   432  		return v, nil
   433  	case protocol.OptionBestEffort:
   434  		c.s.Lock()
   435  		v := c.bestEffort
   436  		c.s.Unlock()
   437  		return v, nil
   438  	case protocol.OptionFailNoPeers:
   439  		c.s.Lock()
   440  		v := c.failNoPeers
   441  		c.s.Unlock()
   442  		return v, nil
   443  	}
   444  
   445  	return nil, protocol.ErrBadOption
   446  }
   447  
   448  func (c *context) Close() error {
   449  	s := c.s
   450  	c.s.Lock()
   451  	defer c.s.Unlock()
   452  	if c.closed {
   453  		return protocol.ErrClosed
   454  	}
   455  	c.closed = true
   456  	c.cancel()
   457  	delete(s.contexts, c)
   458  	return nil
   459  }
   460  
   461  func (s *socket) GetOption(option string) (interface{}, error) {
   462  	switch option {
   463  	case protocol.OptionRaw:
   464  		return false, nil
   465  	default:
   466  		return s.defCtx.GetOption(option)
   467  	}
   468  }
   469  func (s *socket) SetOption(option string, value interface{}) error {
   470  	return s.defCtx.SetOption(option, value)
   471  }
   472  
   473  func (s *socket) SendMsg(m *protocol.Message) error {
   474  	return s.defCtx.SendMsg(m)
   475  }
   476  
   477  func (s *socket) RecvMsg() (*protocol.Message, error) {
   478  	return s.defCtx.RecvMsg()
   479  }
   480  
   481  func (s *socket) Close() error {
   482  	s.Lock()
   483  
   484  	if s.closed {
   485  		s.Unlock()
   486  		return protocol.ErrClosed
   487  	}
   488  	s.closed = true
   489  	for c := range s.contexts {
   490  		c.closed = true
   491  		c.cancel()
   492  		delete(s.contexts, c)
   493  	}
   494  	s.Unlock()
   495  	return nil
   496  }
   497  
   498  func (s *socket) OpenContext() (protocol.Context, error) {
   499  	s.Lock()
   500  	defer s.Unlock()
   501  	if s.closed {
   502  		return nil, protocol.ErrClosed
   503  	}
   504  	c := &context{
   505  		s:             s,
   506  		cond:          sync.NewCond(s),
   507  		bestEffort:    s.defCtx.bestEffort,
   508  		resendTime:    s.defCtx.resendTime,
   509  		sendExpire:    s.defCtx.sendExpire,
   510  		receiveExpire: s.defCtx.receiveExpire,
   511  		failNoPeers:   s.defCtx.failNoPeers,
   512  	}
   513  	s.contexts[c] = struct{}{}
   514  	return c, nil
   515  }
   516  
   517  func (s *socket) AddPipe(pp protocol.Pipe) error {
   518  	p := &pipe{
   519  		p: pp,
   520  		s: s,
   521  	}
   522  	pp.SetPrivate(p)
   523  	s.Lock()
   524  	defer s.Unlock()
   525  	if s.closed {
   526  		return protocol.ErrClosed
   527  	}
   528  	s.readyQ = append(s.readyQ, p)
   529  	s.send()
   530  	s.pipes[pp.ID()] = p
   531  	go p.receiver()
   532  	return nil
   533  }
   534  
   535  func (s *socket) RemovePipe(pp protocol.Pipe) {
   536  	p := pp.GetPrivate().(*pipe)
   537  	s.Lock()
   538  	p.closed = true
   539  	for i, rp := range s.readyQ {
   540  		if p == rp {
   541  			s.readyQ = append(s.readyQ[:i], s.readyQ[i+1:]...)
   542  		}
   543  	}
   544  	delete(s.pipes, pp.ID())
   545  	for c := range s.contexts {
   546  		if c.failNoPeers && len(s.pipes) == 0 {
   547  			c.cancel()
   548  		} else if c.lastPipe == p && c.reqMsg != nil {
   549  			// We are closing this pipe, so we need to
   550  			// immediately reschedule it.
   551  			c.lastPipe = nil
   552  			id := c.reqID
   553  			// If there is no resend time, then we need to simply
   554  			// discard the message, because it's not necessarily idempotent.
   555  			if c.resendTime == 0 {
   556  				c.cancel()
   557  			} else {
   558  				c.cancelSend()
   559  				go c.resendMessage(id)
   560  			}
   561  		}
   562  	}
   563  	s.Unlock()
   564  }
   565  
   566  func (*socket) Info() protocol.Info {
   567  	return protocol.Info{
   568  		Self:     Self,
   569  		Peer:     Peer,
   570  		SelfName: SelfName,
   571  		PeerName: PeerName,
   572  	}
   573  }
   574  
   575  // NewProtocol allocates a new protocol implementation.
   576  func NewProtocol() protocol.Protocol {
   577  	s := &socket{
   578  		nextID:   uint32(time.Now().UnixNano()), // quasi-random
   579  		contexts: make(map[*context]struct{}),
   580  		ctxByID:  make(map[uint32]*context),
   581  		pipes:    make(map[uint32]*pipe),
   582  	}
   583  	s.defCtx = &context{
   584  		s:          s,
   585  		cond:       sync.NewCond(s),
   586  		resendTime: time.Minute,
   587  	}
   588  	s.contexts[s.defCtx] = struct{}{}
   589  	return s
   590  }
   591  
   592  // NewSocket allocates a new Socket using the REQ protocol.
   593  func NewSocket() (protocol.Socket, error) {
   594  	return protocol.MakeSocket(NewProtocol()), nil
   595  }