go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/xsurveyor/xsurveyor.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 xsurveyor implements the SURVEYOR protocol. This sends messages
    16  // out to RESPONDENT partners, and receives their responses.
    17  package xsurveyor
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"go.nanomsg.org/mangos/v3/protocol"
    24  )
    25  
    26  // Protocol identity information.
    27  const (
    28  	Self     = protocol.ProtoSurveyor
    29  	Peer     = protocol.ProtoRespondent
    30  	SelfName = "surveyor"
    31  	PeerName = "respondent"
    32  )
    33  
    34  type pipe struct {
    35  	p      protocol.Pipe
    36  	s      *socket
    37  	closeQ chan struct{}
    38  	sendQ  chan *protocol.Message
    39  }
    40  
    41  type socket struct {
    42  	closed     bool
    43  	pipes      map[uint32]*pipe
    44  	recvQLen   int
    45  	sendQLen   int
    46  	recvExpire time.Duration
    47  	recvQ      chan *protocol.Message
    48  	closeQ     chan struct{}
    49  	sizeQ      chan struct{}
    50  	sync.Mutex
    51  }
    52  
    53  var (
    54  	nilQ <-chan time.Time
    55  )
    56  
    57  const defaultQLen = 128
    58  
    59  func (s *socket) SendMsg(m *protocol.Message) error {
    60  	s.Lock()
    61  	if s.closed {
    62  		s.Unlock()
    63  		return protocol.ErrClosed
    64  	}
    65  
    66  	// This could benefit from optimization to avoid useless duplicates.
    67  	for _, p := range s.pipes {
    68  		m.Clone()
    69  		select {
    70  		case p.sendQ <- m:
    71  		default:
    72  			m.Free()
    73  		}
    74  	}
    75  	s.Unlock()
    76  	m.Free()
    77  	return nil
    78  }
    79  
    80  func (s *socket) RecvMsg() (*protocol.Message, error) {
    81  	// For now this uses a simple unified queue for the entire
    82  	// socket.  Later we can look at moving this to priority queues
    83  	// based on socket pipes.
    84  
    85  	for {
    86  		s.Lock()
    87  		timeQ := nilQ
    88  		if s.recvExpire > 0 {
    89  			timeQ = time.After(s.recvExpire)
    90  		}
    91  		recvQ := s.recvQ
    92  		sizeQ := s.sizeQ
    93  		closeQ := s.closeQ
    94  		s.Unlock()
    95  
    96  		select {
    97  		case m := <-recvQ:
    98  			return m, nil
    99  		case <-closeQ:
   100  			return nil, protocol.ErrClosed
   101  		case <-timeQ:
   102  			return nil, protocol.ErrRecvTimeout
   103  		case <-sizeQ:
   104  			continue
   105  		}
   106  	}
   107  }
   108  
   109  func (s *socket) SetOption(name string, value interface{}) error {
   110  	switch name {
   111  
   112  	case protocol.OptionRecvDeadline:
   113  		if v, ok := value.(time.Duration); ok {
   114  			s.Lock()
   115  			s.recvExpire = v
   116  			s.Unlock()
   117  			return nil
   118  		}
   119  		return protocol.ErrBadValue
   120  
   121  	case protocol.OptionWriteQLen:
   122  		if v, ok := value.(int); ok && v >= 0 {
   123  			s.Lock()
   124  			s.sendQLen = v
   125  			s.Unlock()
   126  			return nil
   127  		}
   128  		return protocol.ErrBadValue
   129  
   130  	case protocol.OptionReadQLen:
   131  		if v, ok := value.(int); ok && v >= 0 {
   132  			recvQ := make(chan *protocol.Message, v)
   133  			sizeQ := make(chan struct{})
   134  			s.Lock()
   135  			s.recvQ = recvQ
   136  			sizeQ, s.sizeQ = s.sizeQ, sizeQ
   137  			s.recvQLen = v
   138  			s.Unlock()
   139  			close(sizeQ)
   140  			// Messages in the old channel "leak", but should be
   141  			// picked up by garbage collection.  This is not
   142  			// a big deal for us.
   143  			return nil
   144  		}
   145  		return protocol.ErrBadValue
   146  	}
   147  
   148  	return protocol.ErrBadOption
   149  }
   150  
   151  func (s *socket) GetOption(option string) (interface{}, error) {
   152  	switch option {
   153  	case protocol.OptionRaw:
   154  		return true, nil
   155  	case protocol.OptionRecvDeadline:
   156  		s.Lock()
   157  		v := s.recvExpire
   158  		s.Unlock()
   159  		return v, nil
   160  	case protocol.OptionWriteQLen:
   161  		s.Lock()
   162  		v := s.sendQLen
   163  		s.Unlock()
   164  		return v, nil
   165  	case protocol.OptionReadQLen:
   166  		s.Lock()
   167  		v := s.recvQLen
   168  		s.Unlock()
   169  		return v, nil
   170  	}
   171  
   172  	return nil, protocol.ErrBadOption
   173  }
   174  
   175  func (s *socket) AddPipe(pp protocol.Pipe) error {
   176  	p := &pipe{
   177  		p:      pp,
   178  		s:      s,
   179  		closeQ: make(chan struct{}),
   180  		sendQ:  make(chan *protocol.Message, s.sendQLen),
   181  	}
   182  	pp.SetPrivate(p)
   183  	s.Lock()
   184  	defer s.Unlock()
   185  	if s.closed {
   186  		return protocol.ErrClosed
   187  	}
   188  	s.pipes[pp.ID()] = p
   189  
   190  	go p.sender()
   191  	go p.receiver()
   192  	return nil
   193  }
   194  
   195  func (s *socket) RemovePipe(pp protocol.Pipe) {
   196  	p := pp.GetPrivate().(*pipe)
   197  	close(p.closeQ)
   198  	s.Lock()
   199  	delete(p.s.pipes, p.p.ID())
   200  	s.Unlock()
   201  }
   202  
   203  func (s *socket) OpenContext() (protocol.Context, error) {
   204  	return nil, protocol.ErrProtoOp
   205  }
   206  
   207  func (*socket) Info() protocol.Info {
   208  	return protocol.Info{
   209  		Self:     Self,
   210  		Peer:     Peer,
   211  		SelfName: SelfName,
   212  		PeerName: PeerName,
   213  	}
   214  }
   215  
   216  func (s *socket) Close() error {
   217  	s.Lock()
   218  
   219  	if s.closed {
   220  		s.Unlock()
   221  		return protocol.ErrClosed
   222  	}
   223  	s.closed = true
   224  	s.Unlock()
   225  
   226  	close(s.closeQ)
   227  	return nil
   228  
   229  }
   230  
   231  func (p *pipe) sender() {
   232  outer:
   233  	for {
   234  		var m *protocol.Message
   235  		select {
   236  		case <-p.closeQ:
   237  			break outer
   238  		case m = <-p.sendQ:
   239  		}
   240  
   241  		if err := p.p.SendMsg(m); err != nil {
   242  			m.Free()
   243  			break
   244  		}
   245  	}
   246  	p.close()
   247  }
   248  
   249  func (p *pipe) receiver() {
   250  	s := p.s
   251  outer:
   252  	for {
   253  		m := p.p.RecvMsg()
   254  		if m == nil {
   255  			break
   256  		}
   257  
   258  		if len(m.Body) < 4 {
   259  			m.Free()
   260  			continue
   261  		}
   262  
   263  		m.Header = m.Body[:4]
   264  		m.Body = m.Body[4:]
   265  
   266  		s.Lock()
   267  		recvQ := s.recvQ
   268  		sizeQ := s.sizeQ
   269  		s.Unlock()
   270  
   271  		select {
   272  		case recvQ <- m:
   273  		case <-p.closeQ:
   274  			m.Free()
   275  			break outer
   276  		case <-sizeQ:
   277  			m.Free()
   278  		}
   279  	}
   280  	p.close()
   281  }
   282  
   283  func (p *pipe) close() {
   284  	_ = p.p.Close()
   285  }
   286  
   287  // NewProtocol returns a new protocol implementation.
   288  func NewProtocol() protocol.Protocol {
   289  	s := &socket{
   290  		pipes:    make(map[uint32]*pipe),
   291  		closeQ:   make(chan struct{}),
   292  		sizeQ:    make(chan struct{}),
   293  		recvQ:    make(chan *protocol.Message, defaultQLen),
   294  		sendQLen: defaultQLen,
   295  		recvQLen: defaultQLen,
   296  	}
   297  	return s
   298  }
   299  
   300  // NewSocket allocates a new Socket using the RESPONDENT protocol.
   301  func NewSocket() (protocol.Socket, error) {
   302  	return protocol.MakeSocket(NewProtocol()), nil
   303  }