github.com/gdamore/mangos@v1.4.0/protocol/req/req.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 req implements the REQ protocol, which is the request side of
    16  // the request/response pattern.  (REP is the response.)
    17  package req
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  // req is an implementation of the req protocol.
    28  type req struct {
    29  	sync.Mutex
    30  	sock   mangos.ProtocolSocket
    31  	eps    map[uint32]*reqEp
    32  	resend chan *mangos.Message
    33  	raw    bool
    34  	retry  time.Duration
    35  	nextid uint32
    36  	waker  *time.Timer
    37  	w      mangos.Waiter
    38  	init   sync.Once
    39  
    40  	// fields describing the outstanding request
    41  	reqmsg *mangos.Message
    42  	reqid  uint32
    43  }
    44  
    45  type reqEp struct {
    46  	ep mangos.Endpoint
    47  	cq chan struct{}
    48  }
    49  
    50  func (r *req) Init(socket mangos.ProtocolSocket) {
    51  	r.sock = socket
    52  	r.eps = make(map[uint32]*reqEp)
    53  	r.resend = make(chan *mangos.Message)
    54  	r.w.Init()
    55  
    56  	r.nextid = uint32(time.Now().UnixNano()) // quasi-random
    57  	r.retry = time.Minute * 1                // retry after a minute
    58  	r.waker = time.NewTimer(r.retry)
    59  	r.waker.Stop()
    60  	r.sock.SetRecvError(mangos.ErrProtoState)
    61  }
    62  
    63  func (r *req) Shutdown(expire time.Time) {
    64  	r.w.WaitAbsTimeout(expire)
    65  }
    66  
    67  // nextID returns the next request ID.
    68  func (r *req) nextID() uint32 {
    69  	// The high order bit is "special", and must always be set.  (This is
    70  	// how the peer will detect the end of the backtrace.)
    71  	v := r.nextid | 0x80000000
    72  	r.nextid++
    73  	return v
    74  }
    75  
    76  // resend sends the request message again, after a timer has expired.
    77  func (r *req) resender() {
    78  
    79  	defer r.w.Done()
    80  	cq := r.sock.CloseChannel()
    81  
    82  	for {
    83  		select {
    84  		case <-r.waker.C:
    85  		case <-cq:
    86  			return
    87  		}
    88  
    89  		r.Lock()
    90  		m := r.reqmsg
    91  		if m == nil {
    92  			r.Unlock()
    93  			continue
    94  		}
    95  		m = m.Dup()
    96  		r.Unlock()
    97  
    98  		r.resend <- m
    99  		r.Lock()
   100  		if r.retry > 0 {
   101  			r.waker.Reset(r.retry)
   102  		} else {
   103  			r.waker.Stop()
   104  		}
   105  		r.Unlock()
   106  	}
   107  }
   108  
   109  func (r *req) receiver(ep mangos.Endpoint) {
   110  	rq := r.sock.RecvChannel()
   111  	cq := r.sock.CloseChannel()
   112  
   113  	for {
   114  		m := ep.RecvMsg()
   115  		if m == nil {
   116  			break
   117  		}
   118  
   119  		if len(m.Body) < 4 {
   120  			m.Free()
   121  			continue
   122  		}
   123  		m.Header = append(m.Header, m.Body[:4]...)
   124  		m.Body = m.Body[4:]
   125  
   126  		select {
   127  		case rq <- m:
   128  		case <-cq:
   129  			m.Free()
   130  			break
   131  		}
   132  	}
   133  }
   134  
   135  func (r *req) sender(pe *reqEp) {
   136  
   137  	// NB: Because this function is only called when an endpoint is
   138  	// added, we can reasonably safely cache the channels -- they won't
   139  	// be changing after this point.
   140  
   141  	defer r.w.Done()
   142  	sq := r.sock.SendChannel()
   143  	cq := r.sock.CloseChannel()
   144  	rq := r.resend
   145  
   146  	for {
   147  		var m *mangos.Message
   148  
   149  		select {
   150  		case m = <-rq:
   151  		case m = <-sq:
   152  		case <-cq:
   153  			return
   154  		case <-pe.cq:
   155  			return
   156  		}
   157  
   158  		if pe.ep.SendMsg(m) != nil {
   159  			r.resend <- m
   160  			break
   161  		}
   162  	}
   163  }
   164  
   165  func (*req) Number() uint16 {
   166  	return mangos.ProtoReq
   167  }
   168  
   169  func (*req) PeerNumber() uint16 {
   170  	return mangos.ProtoRep
   171  }
   172  
   173  func (*req) Name() string {
   174  	return "req"
   175  }
   176  
   177  func (*req) PeerName() string {
   178  	return "rep"
   179  }
   180  
   181  func (r *req) AddEndpoint(ep mangos.Endpoint) {
   182  
   183  	r.init.Do(func() {
   184  		r.w.Add()
   185  		go r.resender()
   186  	})
   187  
   188  	pe := &reqEp{cq: make(chan struct{}), ep: ep}
   189  	r.Lock()
   190  	r.eps[ep.GetID()] = pe
   191  
   192  	r.Unlock()
   193  	go r.receiver(ep)
   194  	r.w.Add()
   195  	go r.sender(pe)
   196  }
   197  
   198  func (r *req) RemoveEndpoint(ep mangos.Endpoint) {
   199  	id := ep.GetID()
   200  	r.Lock()
   201  	pe := r.eps[id]
   202  	delete(r.eps, id)
   203  	r.Unlock()
   204  	if pe != nil {
   205  		close(pe.cq)
   206  	}
   207  }
   208  
   209  func (r *req) SendHook(m *mangos.Message) bool {
   210  
   211  	if r.raw {
   212  		// Raw mode has no automatic retry, and must include the
   213  		// request id in the header coming down.
   214  		return true
   215  	}
   216  	r.Lock()
   217  	defer r.Unlock()
   218  
   219  	// We need to generate a new request id, and append it to the header.
   220  	r.reqid = r.nextID()
   221  	v := r.reqid
   222  	m.Header = append(m.Header,
   223  		byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   224  
   225  	r.reqmsg = m.Dup()
   226  
   227  	// Schedule a retry, in case we don't get a reply.
   228  	if r.retry > 0 {
   229  		r.waker.Reset(r.retry)
   230  	} else {
   231  		r.waker.Stop()
   232  	}
   233  
   234  	r.sock.SetRecvError(nil)
   235  
   236  	return true
   237  }
   238  
   239  func (r *req) RecvHook(m *mangos.Message) bool {
   240  	if r.raw {
   241  		// Raw mode just passes up messages unmolested.
   242  		return true
   243  	}
   244  	r.Lock()
   245  	defer r.Unlock()
   246  	if len(m.Header) < 4 {
   247  		return false
   248  	}
   249  	if r.reqmsg == nil {
   250  		return false
   251  	}
   252  	if binary.BigEndian.Uint32(m.Header) != r.reqid {
   253  		return false
   254  	}
   255  	r.waker.Stop()
   256  	m = r.reqmsg
   257  	r.reqmsg = nil
   258  	m.Free()
   259  	r.sock.SetRecvError(mangos.ErrProtoState)
   260  	return true
   261  }
   262  
   263  func (r *req) SetOption(option string, value interface{}) error {
   264  	var ok bool
   265  	switch option {
   266  	case mangos.OptionRaw:
   267  		if r.raw, ok = value.(bool); !ok {
   268  			return mangos.ErrBadValue
   269  		}
   270  		if r.raw {
   271  			r.sock.SetRecvError(nil)
   272  		} else {
   273  			r.sock.SetRecvError(mangos.ErrProtoState)
   274  		}
   275  		return nil
   276  	case mangos.OptionRetryTime:
   277  		r.Lock()
   278  		r.retry, ok = value.(time.Duration)
   279  		r.Unlock()
   280  		if !ok {
   281  			return mangos.ErrBadValue
   282  		}
   283  		return nil
   284  	default:
   285  		return mangos.ErrBadOption
   286  	}
   287  }
   288  
   289  func (r *req) GetOption(option string) (interface{}, error) {
   290  	switch option {
   291  	case mangos.OptionRaw:
   292  		return r.raw, nil
   293  	case mangos.OptionRetryTime:
   294  		r.Lock()
   295  		v := r.retry
   296  		r.Unlock()
   297  		return v, nil
   298  	default:
   299  		return nil, mangos.ErrBadOption
   300  	}
   301  }
   302  
   303  // NewSocket allocates a new Socket using the REQ protocol.
   304  func NewSocket() (mangos.Socket, error) {
   305  	return mangos.MakeSocket(&req{}), nil
   306  }