go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/xsub/xsub.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 xsub implements the raw SUB protocol. This protocol simply
    16  // passes through all messages received, and does not filter them.
    17  package xsub
    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.ProtoSub
    29  	Peer     = protocol.ProtoPub
    30  	SelfName = "sub"
    31  	PeerName = "pub"
    32  )
    33  
    34  type pipe struct {
    35  	p protocol.Pipe
    36  	s *socket
    37  }
    38  
    39  type socket struct {
    40  	closed     bool
    41  	closeQ     chan struct{}
    42  	recvQLen   int
    43  	recvExpire time.Duration
    44  	recvQ      chan *protocol.Message
    45  	sizeQ      chan struct{}
    46  	sync.Mutex
    47  }
    48  
    49  var (
    50  	nilQ <-chan time.Time
    51  )
    52  
    53  const defaultQLen = 128
    54  
    55  func (s *socket) SendMsg(*protocol.Message) error {
    56  	return protocol.ErrProtoOp
    57  }
    58  
    59  func (s *socket) RecvMsg() (*protocol.Message, error) {
    60  	// For now this uses a simple unified queue for the entire
    61  	// socket.  Later we can look at moving this to priority queues
    62  	// based on socket pipes.
    63  	timeQ := nilQ
    64  	for {
    65  		s.Lock()
    66  		if s.recvExpire > 0 {
    67  			timeQ = time.After(s.recvExpire)
    68  		}
    69  		closeQ := s.closeQ
    70  		sizeQ := s.sizeQ
    71  		recvQ := s.recvQ
    72  		s.Unlock()
    73  		select {
    74  		case <-closeQ:
    75  			return nil, protocol.ErrClosed
    76  		case <-timeQ:
    77  			return nil, protocol.ErrRecvTimeout
    78  		case <-sizeQ:
    79  			continue
    80  		case m := <-recvQ:
    81  			return m, nil
    82  		}
    83  	}
    84  }
    85  
    86  func (s *socket) SetOption(name string, value interface{}) error {
    87  	switch name {
    88  
    89  	case protocol.OptionRecvDeadline:
    90  		if v, ok := value.(time.Duration); ok {
    91  			s.Lock()
    92  			s.recvExpire = v
    93  			s.Unlock()
    94  			return nil
    95  		}
    96  		return protocol.ErrBadValue
    97  
    98  	case protocol.OptionReadQLen:
    99  		if v, ok := value.(int); ok && v >= 0 {
   100  			recvQ := make(chan *protocol.Message, v)
   101  			sizeQ := make(chan struct{})
   102  			s.Lock()
   103  			s.recvQ = recvQ
   104  			sizeQ, s.sizeQ = s.sizeQ, sizeQ
   105  			s.recvQLen = v
   106  			s.Unlock()
   107  			close(sizeQ)
   108  
   109  			// This leaks a few messages.  But it doesn't really
   110  			// matter.  Resizing the queue tosses the messages.
   111  			return nil
   112  		}
   113  		return protocol.ErrBadValue
   114  	}
   115  
   116  	return protocol.ErrBadOption
   117  }
   118  
   119  func (s *socket) GetOption(option string) (interface{}, error) {
   120  	switch option {
   121  	case protocol.OptionRaw:
   122  		return true, nil
   123  	case protocol.OptionRecvDeadline:
   124  		s.Lock()
   125  		v := s.recvExpire
   126  		s.Unlock()
   127  		return v, nil
   128  	case protocol.OptionReadQLen:
   129  		s.Lock()
   130  		v := s.recvQLen
   131  		s.Unlock()
   132  		return v, nil
   133  	}
   134  
   135  	return nil, protocol.ErrBadOption
   136  }
   137  
   138  func (s *socket) AddPipe(pp protocol.Pipe) error {
   139  	p := &pipe{
   140  		p: pp,
   141  		s: s,
   142  	}
   143  	s.Lock()
   144  	defer s.Unlock()
   145  	if s.closed {
   146  		return protocol.ErrClosed
   147  	}
   148  	go p.receiver()
   149  	return nil
   150  }
   151  
   152  func (s *socket) RemovePipe(protocol.Pipe) {
   153  }
   154  
   155  func (s *socket) OpenContext() (protocol.Context, error) {
   156  	return nil, protocol.ErrProtoOp
   157  }
   158  
   159  func (*socket) Info() protocol.Info {
   160  	return protocol.Info{
   161  		Self:     Self,
   162  		Peer:     Peer,
   163  		SelfName: SelfName,
   164  		PeerName: PeerName,
   165  	}
   166  }
   167  
   168  func (s *socket) Close() error {
   169  	s.Lock()
   170  	if s.closed {
   171  		s.Unlock()
   172  		return protocol.ErrClosed
   173  	}
   174  	s.closed = true
   175  	s.Unlock()
   176  	close(s.closeQ)
   177  	return nil
   178  }
   179  
   180  func (p *pipe) receiver() {
   181  	s := p.s
   182  	for {
   183  		m := p.p.RecvMsg()
   184  		if m == nil {
   185  			break
   186  		}
   187  
   188  		s.Lock()
   189  		recvQ := s.recvQ
   190  		s.Unlock()
   191  
   192  		// No need to test for resizing or close here, because we
   193  		// never block anyway.
   194  
   195  		select {
   196  		case recvQ <- m:
   197  		default:
   198  			m.Free()
   199  		}
   200  	}
   201  	p.close()
   202  }
   203  
   204  func (p *pipe) close() {
   205  	_ = p.p.Close()
   206  }
   207  
   208  // NewProtocol returns a new protocol implementation.
   209  func NewProtocol() protocol.Protocol {
   210  	s := &socket{
   211  		closeQ:   make(chan struct{}),
   212  		sizeQ:    make(chan struct{}),
   213  		recvQ:    make(chan *protocol.Message, defaultQLen),
   214  		recvQLen: defaultQLen,
   215  	}
   216  	return s
   217  }
   218  
   219  // NewSocket allocates a raw Socket using the PULL protocol.
   220  func NewSocket() (protocol.Socket, error) {
   221  	return protocol.MakeSocket(NewProtocol()), nil
   222  }