nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/protocol/xpull/xpull.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 xpull implements the PULL protocol. This read only protocol
    16  // simply receives messages from pipes.
    17  package xpull
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"nanomsg.org/go/mangos/v2/protocol"
    24  )
    25  
    26  // Protocol identity information.
    27  const (
    28  	Self     = protocol.ProtoPull
    29  	Peer     = protocol.ProtoPush
    30  	SelfName = "pull"
    31  	PeerName = "push"
    32  )
    33  
    34  type pipe struct {
    35  	p      protocol.Pipe
    36  	s      *socket
    37  	closeQ chan struct{}
    38  }
    39  
    40  type socket struct {
    41  	closed         bool
    42  	closeQ         chan struct{}
    43  	sizeQ          chan struct{}
    44  	recvQ          chan *protocol.Message
    45  	recvQLen       int
    46  	resizeDiscards bool // only for testing (facilitates coverage)
    47  	recvExpire     time.Duration
    48  	sync.Mutex
    49  }
    50  
    51  var (
    52  	nilQ <-chan time.Time
    53  )
    54  
    55  const defaultQLen = 128
    56  
    57  func (s *socket) SendMsg(m *protocol.Message) error {
    58  	return protocol.ErrProtoOp
    59  }
    60  
    61  func (s *socket) RecvMsg() (*protocol.Message, error) {
    62  	// For now this uses a simple unified queue for the entire
    63  	// socket.  Later we can look at moving this to priority queues
    64  	// based on socket pipes.
    65  	tq := nilQ
    66  	for {
    67  		s.Lock()
    68  		if s.recvExpire > 0 {
    69  			tq = time.After(s.recvExpire)
    70  		}
    71  		cq := s.closeQ
    72  		rq := s.recvQ
    73  		zq := s.sizeQ
    74  		s.Unlock()
    75  		select {
    76  		case <-cq:
    77  			return nil, protocol.ErrClosed
    78  		case <-tq:
    79  			return nil, protocol.ErrRecvTimeout
    80  		case <-zq:
    81  			continue
    82  		case m := <-rq:
    83  			return m, nil
    84  		}
    85  	}
    86  }
    87  
    88  func (s *socket) SetOption(name string, value interface{}) error {
    89  	switch name {
    90  
    91  	case protocol.OptionRecvDeadline:
    92  		if v, ok := value.(time.Duration); ok {
    93  			s.Lock()
    94  			s.recvExpire = v
    95  			s.Unlock()
    96  			return nil
    97  		}
    98  		return protocol.ErrBadValue
    99  
   100  	case protocol.OptionReadQLen:
   101  		if v, ok := value.(int); ok && v >= 0 {
   102  			newQ := make(chan *protocol.Message, v)
   103  			s.Lock()
   104  			s.recvQLen = v
   105  			oldQ := s.recvQ
   106  			s.recvQ = newQ
   107  			zq := s.sizeQ
   108  			s.sizeQ = make(chan struct{})
   109  			discard := s.resizeDiscards
   110  			s.Unlock()
   111  
   112  			close(zq)
   113  			if !discard {
   114  				for {
   115  					var m *protocol.Message
   116  					select {
   117  					case m = <-oldQ:
   118  					default:
   119  					}
   120  					if m == nil {
   121  						break
   122  					}
   123  					select {
   124  					case newQ <- m:
   125  					default:
   126  						m.Free()
   127  					}
   128  				}
   129  			}
   130  			return nil
   131  		}
   132  		return protocol.ErrBadValue
   133  
   134  	case "_resizeDiscards":
   135  		// This option is here to facilitate testing.
   136  		if v, ok := value.(bool); ok {
   137  			s.Lock()
   138  			s.resizeDiscards = v
   139  			s.Unlock()
   140  		}
   141  		return nil
   142  	}
   143  
   144  	return protocol.ErrBadOption
   145  }
   146  
   147  func (s *socket) GetOption(option string) (interface{}, error) {
   148  	switch option {
   149  	case protocol.OptionRaw:
   150  		return true, nil
   151  	case protocol.OptionRecvDeadline:
   152  		s.Lock()
   153  		v := s.recvExpire
   154  		s.Unlock()
   155  		return v, nil
   156  	case protocol.OptionReadQLen:
   157  		s.Lock()
   158  		v := s.recvQLen
   159  		s.Unlock()
   160  		return v, nil
   161  	}
   162  
   163  	return nil, protocol.ErrBadOption
   164  }
   165  
   166  func (s *socket) AddPipe(pp protocol.Pipe) error {
   167  	p := &pipe{
   168  		p:      pp,
   169  		s:      s,
   170  		closeQ: make(chan struct{}),
   171  	}
   172  	pp.SetPrivate(p)
   173  	s.Lock()
   174  	defer s.Unlock()
   175  	if s.closed {
   176  		return protocol.ErrClosed
   177  	}
   178  	go p.receiver()
   179  	return nil
   180  }
   181  
   182  func (s *socket) RemovePipe(pp protocol.Pipe) {
   183  	p := pp.GetPrivate().(*pipe)
   184  	close(p.closeQ)
   185  }
   186  
   187  func (s *socket) OpenContext() (protocol.Context, error) {
   188  	return nil, protocol.ErrProtoOp
   189  }
   190  
   191  func (*socket) Info() protocol.Info {
   192  	return protocol.Info{
   193  		Self:     Self,
   194  		Peer:     Peer,
   195  		SelfName: SelfName,
   196  		PeerName: PeerName,
   197  	}
   198  }
   199  
   200  func (s *socket) Close() error {
   201  	s.Lock()
   202  	if s.closed {
   203  		s.Unlock()
   204  		return protocol.ErrClosed
   205  	}
   206  	s.closed = true
   207  	s.Unlock()
   208  	close(s.closeQ)
   209  	return nil
   210  }
   211  
   212  func (p *pipe) receiver() {
   213  	s := p.s
   214  outer:
   215  	for {
   216  		m := p.p.RecvMsg()
   217  		if m == nil {
   218  			break
   219  		}
   220  
   221  	inner:
   222  		for {
   223  			s.Lock()
   224  			rq := s.recvQ
   225  			zq := s.sizeQ
   226  			s.Unlock()
   227  
   228  			select {
   229  			case rq <- m:
   230  				continue outer
   231  			case <-zq:
   232  				continue inner
   233  			case <-p.closeQ:
   234  				m.Free()
   235  				break outer
   236  			}
   237  		}
   238  	}
   239  	p.close()
   240  }
   241  
   242  func (p *pipe) close() {
   243  	_ = p.p.Close()
   244  }
   245  
   246  // NewProtocol returns a new protocol implementation.
   247  func NewProtocol() protocol.Protocol {
   248  	s := &socket{
   249  		closeQ:   make(chan struct{}),
   250  		sizeQ:    make(chan struct{}),
   251  		recvQ:    make(chan *protocol.Message, defaultQLen),
   252  		recvQLen: defaultQLen,
   253  	}
   254  	return s
   255  }
   256  
   257  // NewSocket allocates a raw Socket using the PULL protocol.
   258  func NewSocket() (protocol.Socket, error) {
   259  	return protocol.MakeSocket(NewProtocol()), nil
   260  }