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