nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/protocol/xrespondent/xrespondent.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 xrespondent implements the raw RESPONDENT protocol, which is the
    16  // response side of survey pattern.  (SURVEYOR is the survey generator.)
    17  package xrespondent
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go/mangos/v2/protocol"
    25  )
    26  
    27  // Protocol identity information.
    28  const (
    29  	Self     = protocol.ProtoRespondent
    30  	Peer     = protocol.ProtoSurveyor
    31  	SelfName = "respondent"
    32  	PeerName = "surveyor"
    33  )
    34  
    35  type pipe struct {
    36  	p      protocol.Pipe
    37  	s      *socket
    38  	closeQ chan struct{}
    39  	sendQ  chan *protocol.Message
    40  }
    41  
    42  type socket struct {
    43  	closed     bool
    44  	closeQ     chan struct{}
    45  	sizeQ      chan struct{}
    46  	recvQ      chan *protocol.Message
    47  	pipes      map[uint32]*pipe
    48  	recvExpire time.Duration
    49  	sendExpire time.Duration
    50  	sendQLen   int
    51  	recvQLen   int
    52  	bestEffort bool
    53  	ttl        int
    54  	sync.Mutex
    55  }
    56  
    57  var (
    58  	nilQ    <-chan time.Time
    59  	closedQ chan time.Time
    60  )
    61  
    62  const defaultQLen = 128
    63  
    64  func init() {
    65  	closedQ = make(chan time.Time)
    66  	close(closedQ)
    67  }
    68  
    69  // SendMessage implements sending a message.  The message must already
    70  // have headers... the first 4 bytes are the identity of the pipe
    71  // we should send to.
    72  func (s *socket) SendMsg(m *protocol.Message) error {
    73  
    74  	s.Lock()
    75  	if s.closed {
    76  		s.Unlock()
    77  		return protocol.ErrClosed
    78  	}
    79  
    80  	if len(m.Header) < 4 {
    81  		s.Unlock()
    82  		m.Free()
    83  		return nil
    84  	}
    85  
    86  	id := binary.BigEndian.Uint32(m.Header)
    87  	hdr := m.Header
    88  	m.Header = m.Header[4:]
    89  
    90  	p, ok := s.pipes[id]
    91  	if !ok {
    92  		s.Unlock()
    93  		m.Free()
    94  		return nil
    95  	}
    96  	bestEffort := s.bestEffort
    97  	tq := nilQ
    98  	if bestEffort {
    99  		tq = closedQ
   100  	} else if s.sendExpire > 0 {
   101  		tq = time.After(s.sendExpire)
   102  	}
   103  	s.Unlock()
   104  
   105  	select {
   106  	case p.sendQ <- m:
   107  		return nil
   108  	case <-p.closeQ:
   109  		m.Free()
   110  		return nil // No way to return the message
   111  	case <-tq:
   112  		if bestEffort {
   113  			m.Free()
   114  			return nil
   115  		}
   116  		// restore the header
   117  		m.Header = hdr
   118  		return protocol.ErrSendTimeout
   119  	}
   120  }
   121  
   122  func (s *socket) RecvMsg() (*protocol.Message, error) {
   123  	timeQ := nilQ
   124  	s.Lock()
   125  	if s.recvExpire > 0 {
   126  		timeQ = time.After(s.recvExpire)
   127  	}
   128  	recvQ := s.recvQ
   129  	sizeQ := s.sizeQ
   130  	closeQ := s.closeQ
   131  	s.Unlock()
   132  
   133  	for {
   134  		select {
   135  		case <-closeQ:
   136  			return nil, protocol.ErrClosed
   137  		case <-timeQ:
   138  			return nil, protocol.ErrRecvTimeout
   139  		case m := <-recvQ:
   140  			return m, nil
   141  		case <-sizeQ:
   142  			s.Lock()
   143  			recvQ = s.recvQ
   144  			sizeQ = s.sizeQ
   145  			s.Unlock()
   146  		}
   147  	}
   148  }
   149  
   150  func (p *pipe) receiver() {
   151  	s := p.s
   152  outer:
   153  	for {
   154  
   155  		m := p.p.RecvMsg()
   156  		if m == nil {
   157  			break
   158  		}
   159  		if len(m.Body) < 4 {
   160  			m.Free()
   161  			continue
   162  		}
   163  
   164  		// Outer most value of header is pipe ID
   165  		m.Header = append(make([]byte, 4), m.Header...)
   166  		binary.BigEndian.PutUint32(m.Header, p.p.ID())
   167  
   168  		s.Lock()
   169  		ttl := s.ttl
   170  		s.Unlock()
   171  
   172  		hops := 1
   173  		finish := false
   174  		for !finish {
   175  			if hops >= ttl {
   176  				m.Free()
   177  				continue outer
   178  			}
   179  			hops++
   180  			if len(m.Body) < 4 {
   181  				m.Free() // Garbled!
   182  				continue outer
   183  			}
   184  			if m.Body[0]&0x80 != 0 {
   185  				// High order bit set indicates ID and end of
   186  				// message headers.
   187  				finish = true
   188  			}
   189  			m.Header = append(m.Header, m.Body[:4]...)
   190  			m.Body = m.Body[4:]
   191  		}
   192  
   193  		s.Lock()
   194  		recvQ := s.recvQ
   195  		sizeQ := s.sizeQ
   196  		s.Unlock()
   197  
   198  		select {
   199  		case recvQ <- m:
   200  			continue
   201  		case <-sizeQ:
   202  			continue
   203  		case <-p.closeQ:
   204  			m.Free()
   205  			break outer
   206  		}
   207  	}
   208  	p.close()
   209  }
   210  
   211  // This is a puller, and doesn't permit for priorities.  We might want
   212  // to refactor this to use a push based scheme later.
   213  func (p *pipe) sender() {
   214  outer:
   215  	for {
   216  		var m *protocol.Message
   217  		select {
   218  		case m = <-p.sendQ:
   219  		case <-p.closeQ:
   220  			break outer
   221  		}
   222  
   223  		if e := p.p.SendMsg(m); e != nil {
   224  			break
   225  		}
   226  	}
   227  	p.close()
   228  }
   229  
   230  func (p *pipe) close() {
   231  	_ = p.p.Close()
   232  }
   233  
   234  func (s *socket) SetOption(name string, value interface{}) error {
   235  	switch name {
   236  
   237  	case protocol.OptionTTL:
   238  		if v, ok := value.(int); ok && v > 0 && v < 256 {
   239  			s.Lock()
   240  			s.ttl = v
   241  			s.Unlock()
   242  			return nil
   243  		}
   244  		return protocol.ErrBadValue
   245  
   246  	case protocol.OptionRecvDeadline:
   247  		if v, ok := value.(time.Duration); ok {
   248  			s.Lock()
   249  			s.recvExpire = v
   250  			s.Unlock()
   251  			return nil
   252  		}
   253  		return protocol.ErrBadValue
   254  
   255  	case protocol.OptionSendDeadline:
   256  		if v, ok := value.(time.Duration); ok {
   257  			s.Lock()
   258  			s.sendExpire = v
   259  			s.Unlock()
   260  			return nil
   261  		}
   262  		return protocol.ErrBadValue
   263  
   264  	case protocol.OptionBestEffort:
   265  		if v, ok := value.(bool); ok {
   266  			s.Lock()
   267  			s.bestEffort = v
   268  			s.Unlock()
   269  			return nil
   270  		}
   271  		return protocol.ErrBadValue
   272  
   273  	case protocol.OptionWriteQLen:
   274  		if v, ok := value.(int); ok && v >= 0 {
   275  			s.Lock()
   276  			// This does not impact pipes already connected.
   277  			s.sendQLen = v
   278  			s.Unlock()
   279  			return nil
   280  		}
   281  		return protocol.ErrBadValue
   282  
   283  	case protocol.OptionReadQLen:
   284  		if v, ok := value.(int); ok && v >= 0 {
   285  			recvQ := make(chan *protocol.Message, v)
   286  			sizeQ := make(chan struct{})
   287  			s.Lock()
   288  			close(s.sizeQ)
   289  			s.recvQLen = v
   290  			s.recvQ = recvQ
   291  			s.sizeQ = sizeQ
   292  			s.Unlock()
   293  			return nil
   294  		}
   295  		return protocol.ErrBadValue
   296  	}
   297  
   298  	return protocol.ErrBadOption
   299  }
   300  
   301  func (s *socket) GetOption(option string) (interface{}, error) {
   302  	switch option {
   303  	case protocol.OptionRaw:
   304  		return true, nil
   305  	case protocol.OptionTTL:
   306  		s.Lock()
   307  		v := s.ttl
   308  		s.Unlock()
   309  		return v, nil
   310  	case protocol.OptionRecvDeadline:
   311  		s.Lock()
   312  		v := s.recvExpire
   313  		s.Unlock()
   314  		return v, nil
   315  	case protocol.OptionSendDeadline:
   316  		s.Lock()
   317  		v := s.sendExpire
   318  		s.Unlock()
   319  		return v, nil
   320  	case protocol.OptionBestEffort:
   321  		s.Lock()
   322  		v := s.bestEffort
   323  		s.Unlock()
   324  		return v, nil
   325  	case protocol.OptionWriteQLen:
   326  		s.Lock()
   327  		v := s.sendQLen
   328  		s.Unlock()
   329  		return v, nil
   330  	case protocol.OptionReadQLen:
   331  		s.Lock()
   332  		v := s.recvQLen
   333  		s.Unlock()
   334  		return v, nil
   335  	}
   336  
   337  	return nil, protocol.ErrBadOption
   338  }
   339  
   340  func (s *socket) Close() error {
   341  	s.Lock()
   342  	if s.closed {
   343  		s.Unlock()
   344  		return protocol.ErrClosed
   345  	}
   346  	s.closed = true
   347  	s.Unlock()
   348  	close(s.closeQ)
   349  	return nil
   350  }
   351  
   352  func (s *socket) AddPipe(pp protocol.Pipe) error {
   353  	p := &pipe{
   354  		p:      pp,
   355  		s:      s,
   356  		closeQ: make(chan struct{}),
   357  		sendQ:  make(chan *protocol.Message, s.sendQLen),
   358  	}
   359  	pp.SetPrivate(p)
   360  	s.Lock()
   361  	defer s.Unlock()
   362  	if s.closed {
   363  		return protocol.ErrClosed
   364  	}
   365  	s.pipes[pp.ID()] = p
   366  	go p.sender()
   367  	go p.receiver()
   368  	return nil
   369  }
   370  
   371  func (s *socket) RemovePipe(pp protocol.Pipe) {
   372  	p := pp.GetPrivate().(*pipe)
   373  	close(p.closeQ)
   374  	s.Lock()
   375  	delete(s.pipes, p.p.ID())
   376  	s.Unlock()
   377  }
   378  
   379  func (s *socket) OpenContext() (protocol.Context, error) {
   380  	return nil, protocol.ErrProtoOp
   381  }
   382  
   383  func (*socket) Info() protocol.Info {
   384  	return protocol.Info{
   385  		Self:     Self,
   386  		Peer:     Peer,
   387  		SelfName: SelfName,
   388  		PeerName: PeerName,
   389  	}
   390  }
   391  
   392  // NewProtocol returns a new protocol implementation.
   393  func NewProtocol() protocol.Protocol {
   394  	s := &socket{
   395  		pipes:    make(map[uint32]*pipe),
   396  		closeQ:   make(chan struct{}),
   397  		sizeQ:    make(chan struct{}),
   398  		recvQ:    make(chan *protocol.Message, defaultQLen),
   399  		sendQLen: defaultQLen,
   400  		recvQLen: defaultQLen,
   401  		ttl:      8,
   402  	}
   403  	return s
   404  }
   405  
   406  // NewSocket allocates a new Socket using the RESPONDENT protocol.
   407  func NewSocket() (protocol.Socket, error) {
   408  	return protocol.MakeSocket(NewProtocol()), nil
   409  }