github.com/gdamore/mangos@v1.4.0/protocol/rep/rep.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 rep implements the REP protocol, which is the response side of
    16  // the request/response pattern.  (REQ is the request.)
    17  package rep
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  type repEp struct {
    28  	q    chan *mangos.Message
    29  	ep   mangos.Endpoint
    30  	sock mangos.ProtocolSocket
    31  	w    mangos.Waiter
    32  	r    *rep
    33  }
    34  
    35  type rep struct {
    36  	sock         mangos.ProtocolSocket
    37  	eps          map[uint32]*repEp
    38  	backtracebuf []byte
    39  	backtrace    []byte
    40  	backtraceL   sync.Mutex
    41  	raw          bool
    42  	ttl          int
    43  	w            mangos.Waiter
    44  
    45  	sync.Mutex
    46  }
    47  
    48  func (r *rep) Init(sock mangos.ProtocolSocket) {
    49  	r.sock = sock
    50  	r.eps = make(map[uint32]*repEp)
    51  	r.backtracebuf = make([]byte, 64)
    52  	r.ttl = 8 // default specified in the RFC
    53  	r.w.Init()
    54  	r.sock.SetSendError(mangos.ErrProtoState)
    55  	r.w.Add()
    56  	go r.sender()
    57  }
    58  
    59  func (r *rep) Shutdown(expire time.Time) {
    60  
    61  	r.w.WaitAbsTimeout(expire)
    62  
    63  	r.Lock()
    64  	peers := r.eps
    65  	r.eps = make(map[uint32]*repEp)
    66  	r.Unlock()
    67  
    68  	for id, peer := range peers {
    69  		delete(peers, id)
    70  		mangos.DrainChannel(peer.q, expire)
    71  		close(peer.q)
    72  	}
    73  }
    74  
    75  func (pe *repEp) sender() {
    76  	for {
    77  		m := <-pe.q
    78  		if m == nil {
    79  			break
    80  		}
    81  
    82  		if pe.ep.SendMsg(m) != nil {
    83  			m.Free()
    84  			break
    85  		}
    86  	}
    87  }
    88  
    89  func (r *rep) receiver(ep mangos.Endpoint) {
    90  
    91  	rq := r.sock.RecvChannel()
    92  	cq := r.sock.CloseChannel()
    93  
    94  	for {
    95  
    96  		m := ep.RecvMsg()
    97  		if m == nil {
    98  			return
    99  		}
   100  
   101  		v := ep.GetID()
   102  		m.Header = append(m.Header,
   103  			byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   104  
   105  		hops := 0
   106  		// Move backtrace from body to header.
   107  		for {
   108  			if hops >= r.ttl {
   109  				m.Free() // ErrTooManyHops
   110  				return
   111  			}
   112  			hops++
   113  			if len(m.Body) < 4 {
   114  				m.Free() // ErrGarbled
   115  				return
   116  			}
   117  			m.Header = append(m.Header, m.Body[:4]...)
   118  			m.Body = m.Body[4:]
   119  			// Check for high order bit set (0x80000000, big endian)
   120  			if m.Header[len(m.Header)-4]&0x80 != 0 {
   121  				break
   122  			}
   123  		}
   124  
   125  		select {
   126  		case rq <- m:
   127  		case <-cq:
   128  			m.Free()
   129  			return
   130  		}
   131  	}
   132  }
   133  
   134  func (r *rep) sender() {
   135  	defer r.w.Done()
   136  	cq := r.sock.CloseChannel()
   137  	sq := r.sock.SendChannel()
   138  
   139  	for {
   140  		var m *mangos.Message
   141  
   142  		select {
   143  		case m = <-sq:
   144  			if m == nil {
   145  				sq = r.sock.SendChannel()
   146  				continue
   147  			}
   148  		case <-cq:
   149  			return
   150  		}
   151  
   152  		// Lop off the 32-bit peer/pipe ID.  If absent, drop.
   153  		if len(m.Header) < 4 {
   154  			m.Free()
   155  			continue
   156  		}
   157  		id := binary.BigEndian.Uint32(m.Header)
   158  		m.Header = m.Header[4:]
   159  		r.Lock()
   160  		pe := r.eps[id]
   161  		if pe == nil {
   162  			r.Unlock()
   163  			m.Free()
   164  			continue
   165  		}
   166  
   167  		select {
   168  		case pe.q <- m:
   169  		default:
   170  			// If our queue is full, we have no choice but to
   171  			// throw it on the floor.  This shouldn't happen,
   172  			// since each partner should be running synchronously.
   173  			// Devices are a different situation, and this could
   174  			// lead to lossy behavior there.  Initiators will
   175  			// resend if this happens.  Devices need to have deep
   176  			// enough queues and be fast enough to avoid this.
   177  			m.Free()
   178  		}
   179  		r.Unlock()
   180  	}
   181  }
   182  
   183  func (*rep) Number() uint16 {
   184  	return mangos.ProtoRep
   185  }
   186  
   187  func (*rep) PeerNumber() uint16 {
   188  	return mangos.ProtoReq
   189  }
   190  
   191  func (*rep) Name() string {
   192  	return "rep"
   193  }
   194  
   195  func (*rep) PeerName() string {
   196  	return "req"
   197  }
   198  
   199  func (r *rep) AddEndpoint(ep mangos.Endpoint) {
   200  	pe := &repEp{ep: ep, r: r, q: make(chan *mangos.Message, 2)}
   201  	pe.w.Init()
   202  	r.Lock()
   203  	r.eps[ep.GetID()] = pe
   204  	r.Unlock()
   205  	go r.receiver(ep)
   206  	go pe.sender()
   207  }
   208  
   209  func (r *rep) RemoveEndpoint(ep mangos.Endpoint) {
   210  	id := ep.GetID()
   211  
   212  	r.Lock()
   213  	pe := r.eps[id]
   214  	delete(r.eps, id)
   215  
   216  	if pe != nil {
   217  		close(pe.q)
   218  	}
   219  	r.Unlock()
   220  }
   221  
   222  // We save the backtrace from this message.  This means that if the app calls
   223  // Recv before calling Send, the saved backtrace will be lost.  This is how
   224  // the application discards / cancels a request to which it declines to reply.
   225  // This is only done in cooked mode.
   226  func (r *rep) RecvHook(m *mangos.Message) bool {
   227  	if r.raw {
   228  		return true
   229  	}
   230  	r.sock.SetSendError(nil)
   231  	r.backtraceL.Lock()
   232  	r.backtrace = append(r.backtracebuf[0:0], m.Header...)
   233  	r.backtraceL.Unlock()
   234  	m.Header = nil
   235  	return true
   236  }
   237  
   238  func (r *rep) SendHook(m *mangos.Message) bool {
   239  	// Store our saved backtrace.  Note that if none was previously stored,
   240  	// there is no one to reply to, and we drop the message.  We only
   241  	// do this in cooked mode.
   242  	if r.raw {
   243  		return true
   244  	}
   245  	r.sock.SetSendError(mangos.ErrProtoState)
   246  	r.backtraceL.Lock()
   247  	m.Header = append(m.Header[0:0], r.backtrace...)
   248  	r.backtrace = nil
   249  	r.backtraceL.Unlock()
   250  	if m.Header == nil {
   251  		return false
   252  	}
   253  	return true
   254  }
   255  
   256  func (r *rep) SetOption(name string, v interface{}) error {
   257  	var ok bool
   258  	switch name {
   259  	case mangos.OptionRaw:
   260  		if r.raw, ok = v.(bool); !ok {
   261  			return mangos.ErrBadValue
   262  		}
   263  		if r.raw {
   264  			r.sock.SetSendError(nil)
   265  		} else {
   266  			r.sock.SetSendError(mangos.ErrProtoState)
   267  		}
   268  		return nil
   269  	case mangos.OptionTTL:
   270  		if ttl, ok := v.(int); !ok {
   271  			return mangos.ErrBadValue
   272  		} else if ttl < 1 || ttl > 255 {
   273  			return mangos.ErrBadValue
   274  		} else {
   275  			r.ttl = ttl
   276  		}
   277  		return nil
   278  	default:
   279  		return mangos.ErrBadOption
   280  	}
   281  }
   282  
   283  func (r *rep) GetOption(name string) (interface{}, error) {
   284  	switch name {
   285  	case mangos.OptionRaw:
   286  		return r.raw, nil
   287  	case mangos.OptionTTL:
   288  		return r.ttl, nil
   289  	default:
   290  		return nil, mangos.ErrBadOption
   291  	}
   292  }
   293  
   294  // NewSocket allocates a new Socket using the REP protocol.
   295  func NewSocket() (mangos.Socket, error) {
   296  	return mangos.MakeSocket(&rep{}), nil
   297  }