github.com/gdamore/mangos@v1.4.0/protocol/surveyor/surveyor.go (about)

     1  // Copyright 2018 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 surveyor implements the SURVEYOR protocol. This sends messages
    16  // out to RESPONDENT partners, and receives their responses.
    17  package surveyor
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  const defaultSurveyTime = time.Second
    28  
    29  type surveyor struct {
    30  	sock     mangos.ProtocolSocket
    31  	peers    map[uint32]*surveyorP
    32  	raw      bool
    33  	nextID   uint32
    34  	surveyID uint32
    35  	duration time.Duration
    36  	timeout  time.Time
    37  	timer    *time.Timer
    38  	w        mangos.Waiter
    39  	init     sync.Once
    40  	ttl      int
    41  
    42  	sync.Mutex
    43  }
    44  
    45  type surveyorP struct {
    46  	q  chan *mangos.Message
    47  	ep mangos.Endpoint
    48  	x  *surveyor
    49  }
    50  
    51  func (x *surveyor) Init(sock mangos.ProtocolSocket) {
    52  	x.sock = sock
    53  	x.peers = make(map[uint32]*surveyorP)
    54  	x.sock.SetRecvError(mangos.ErrProtoState)
    55  	x.timer = time.AfterFunc(x.duration,
    56  		func() { x.sock.SetRecvError(mangos.ErrProtoState) })
    57  	x.timer.Stop()
    58  	x.w.Init()
    59  	x.w.Add()
    60  	go x.sender()
    61  }
    62  
    63  func (x *surveyor) Shutdown(expire time.Time) {
    64  
    65  	x.w.WaitAbsTimeout(expire)
    66  	x.Lock()
    67  	peers := x.peers
    68  	x.peers = make(map[uint32]*surveyorP)
    69  	x.Unlock()
    70  
    71  	for id, peer := range peers {
    72  		delete(peers, id)
    73  		mangos.DrainChannel(peer.q, expire)
    74  		close(peer.q)
    75  	}
    76  }
    77  
    78  func (x *surveyor) sender() {
    79  	defer x.w.Done()
    80  	cq := x.sock.CloseChannel()
    81  	sq := x.sock.SendChannel()
    82  	for {
    83  		var m *mangos.Message
    84  		select {
    85  		case m = <-sq:
    86  			if m == nil {
    87  				sq = x.sock.SendChannel()
    88  				continue
    89  			}
    90  		case <-cq:
    91  			return
    92  		}
    93  
    94  		x.Lock()
    95  		for _, pe := range x.peers {
    96  			m := m.Dup()
    97  			select {
    98  			case pe.q <- m:
    99  			default:
   100  				m.Free()
   101  			}
   102  		}
   103  		x.Unlock()
   104  	}
   105  }
   106  
   107  // When sending, we should have the survey ID in the header.
   108  func (peer *surveyorP) sender() {
   109  	for {
   110  		if m := <-peer.q; m == nil {
   111  			break
   112  		} else {
   113  			if peer.ep.SendMsg(m) != nil {
   114  				m.Free()
   115  				return
   116  			}
   117  		}
   118  	}
   119  }
   120  
   121  func (peer *surveyorP) receiver() {
   122  
   123  	rq := peer.x.sock.RecvChannel()
   124  	cq := peer.x.sock.CloseChannel()
   125  
   126  	for {
   127  		m := peer.ep.RecvMsg()
   128  		if m == nil {
   129  			return
   130  		}
   131  		if len(m.Body) < 4 {
   132  			m.Free()
   133  			continue
   134  		}
   135  
   136  		// Get survery ID -- this will be passed in the header up
   137  		// to the application.  It should include that in the response.
   138  		m.Header = append(m.Header, m.Body[:4]...)
   139  		m.Body = m.Body[4:]
   140  
   141  		select {
   142  		case rq <- m:
   143  		case <-cq:
   144  			return
   145  		}
   146  	}
   147  }
   148  
   149  func (x *surveyor) AddEndpoint(ep mangos.Endpoint) {
   150  	peer := &surveyorP{ep: ep, x: x, q: make(chan *mangos.Message, 1)}
   151  	x.Lock()
   152  	x.peers[ep.GetID()] = peer
   153  	go peer.receiver()
   154  	go peer.sender()
   155  	x.Unlock()
   156  }
   157  
   158  func (x *surveyor) RemoveEndpoint(ep mangos.Endpoint) {
   159  	id := ep.GetID()
   160  
   161  	x.Lock()
   162  	peer := x.peers[id]
   163  	delete(x.peers, id)
   164  	x.Unlock()
   165  
   166  	if peer != nil {
   167  		close(peer.q)
   168  	}
   169  }
   170  
   171  func (*surveyor) Number() uint16 {
   172  	return mangos.ProtoSurveyor
   173  }
   174  
   175  func (*surveyor) PeerNumber() uint16 {
   176  	return mangos.ProtoRespondent
   177  }
   178  
   179  func (*surveyor) Name() string {
   180  	return "surveyor"
   181  }
   182  
   183  func (*surveyor) PeerName() string {
   184  	return "respondent"
   185  }
   186  
   187  func (x *surveyor) SendHook(m *mangos.Message) bool {
   188  
   189  	if x.raw {
   190  		return true
   191  	}
   192  
   193  	x.Lock()
   194  	x.surveyID = x.nextID | 0x80000000
   195  	x.nextID++
   196  	x.sock.SetRecvError(nil)
   197  	v := x.surveyID
   198  	m.Header = append(m.Header,
   199  		byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   200  
   201  	if x.duration > 0 {
   202  		x.timer.Reset(x.duration)
   203  	}
   204  	x.Unlock()
   205  
   206  	return true
   207  }
   208  
   209  func (x *surveyor) RecvHook(m *mangos.Message) bool {
   210  	if x.raw {
   211  		return true
   212  	}
   213  
   214  	x.Lock()
   215  	defer x.Unlock()
   216  
   217  	if len(m.Header) < 4 {
   218  		return false
   219  	}
   220  	if binary.BigEndian.Uint32(m.Header) != x.surveyID {
   221  		return false
   222  	}
   223  	m.Header = m.Header[4:]
   224  	return true
   225  }
   226  
   227  func (x *surveyor) SetOption(name string, val interface{}) error {
   228  	var ok bool
   229  	switch name {
   230  	case mangos.OptionRaw:
   231  		if x.raw, ok = val.(bool); !ok {
   232  			return mangos.ErrBadValue
   233  		}
   234  		if x.raw {
   235  			x.timer.Stop()
   236  			x.sock.SetRecvError(nil)
   237  		} else {
   238  			x.sock.SetRecvError(mangos.ErrProtoState)
   239  		}
   240  		return nil
   241  	case mangos.OptionSurveyTime:
   242  		x.Lock()
   243  		x.duration, ok = val.(time.Duration)
   244  		x.Unlock()
   245  		if !ok {
   246  			return mangos.ErrBadValue
   247  		}
   248  		return nil
   249  	case mangos.OptionTTL:
   250  		// We don't do anything with this, but support it for
   251  		// symmetry with the respondent socket.
   252  		if ttl, ok := val.(int); !ok {
   253  			return mangos.ErrBadValue
   254  		} else if ttl < 1 || ttl > 255 {
   255  			return mangos.ErrBadValue
   256  		} else {
   257  			x.ttl = ttl
   258  		}
   259  		return nil
   260  	default:
   261  		return mangos.ErrBadOption
   262  	}
   263  }
   264  
   265  func (x *surveyor) GetOption(name string) (interface{}, error) {
   266  	switch name {
   267  	case mangos.OptionRaw:
   268  		return x.raw, nil
   269  	case mangos.OptionSurveyTime:
   270  		x.Lock()
   271  		d := x.duration
   272  		x.Unlock()
   273  		return d, nil
   274  	case mangos.OptionTTL:
   275  		return x.ttl, nil
   276  	default:
   277  		return nil, mangos.ErrBadOption
   278  	}
   279  }
   280  
   281  // NewSocket allocates a new Socket using the SURVEYOR protocol.
   282  func NewSocket() (mangos.Socket, error) {
   283  	return mangos.MakeSocket(&surveyor{duration: defaultSurveyTime}), nil
   284  }