get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/sendq.go (about)

     1  // Copyright 2020-2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"strconv"
    18  	"sync"
    19  )
    20  
    21  type outMsg struct {
    22  	subj string
    23  	rply string
    24  	hdr  []byte
    25  	msg  []byte
    26  }
    27  
    28  type sendq struct {
    29  	mu sync.Mutex
    30  	q  *ipQueue[*outMsg]
    31  	s  *Server
    32  }
    33  
    34  func (s *Server) newSendQ() *sendq {
    35  	sq := &sendq{s: s, q: newIPQueue[*outMsg](s, "SendQ")}
    36  	s.startGoRoutine(sq.internalLoop)
    37  	return sq
    38  }
    39  
    40  func (sq *sendq) internalLoop() {
    41  	sq.mu.Lock()
    42  	s, q := sq.s, sq.q
    43  	sq.mu.Unlock()
    44  
    45  	defer s.grWG.Done()
    46  
    47  	c := s.createInternalSystemClient()
    48  	c.registerWithAccount(s.SystemAccount())
    49  	c.noIcb = true
    50  
    51  	defer c.closeConnection(ClientClosed)
    52  
    53  	// To optimize for not converting a string to a []byte slice.
    54  	var (
    55  		subj [256]byte
    56  		rply [256]byte
    57  		szb  [10]byte
    58  		hdb  [10]byte
    59  	)
    60  
    61  	for s.isRunning() {
    62  		select {
    63  		case <-s.quitCh:
    64  			return
    65  		case <-q.ch:
    66  			pms := q.pop()
    67  			for _, pm := range pms {
    68  				c.pa.subject = append(subj[:0], pm.subj...)
    69  				c.pa.size = len(pm.msg) + len(pm.hdr)
    70  				c.pa.szb = append(szb[:0], strconv.Itoa(c.pa.size)...)
    71  				if len(pm.rply) > 0 {
    72  					c.pa.reply = append(rply[:0], pm.rply...)
    73  				} else {
    74  					c.pa.reply = nil
    75  				}
    76  				var msg []byte
    77  				if len(pm.hdr) > 0 {
    78  					c.pa.hdr = len(pm.hdr)
    79  					c.pa.hdb = append(hdb[:0], strconv.Itoa(c.pa.hdr)...)
    80  					msg = append(pm.hdr, pm.msg...)
    81  					msg = append(msg, _CRLF_...)
    82  				} else {
    83  					c.pa.hdr = -1
    84  					c.pa.hdb = nil
    85  					msg = append(pm.msg, _CRLF_...)
    86  				}
    87  				c.processInboundClientMsg(msg)
    88  				c.pa.szb = nil
    89  				outMsgPool.Put(pm)
    90  			}
    91  			// TODO: should this be in the for-loop instead?
    92  			c.flushClients(0)
    93  			q.recycle(&pms)
    94  		}
    95  	}
    96  }
    97  
    98  var outMsgPool = sync.Pool{
    99  	New: func() any {
   100  		return &outMsg{}
   101  	},
   102  }
   103  
   104  func (sq *sendq) send(subj, rply string, hdr, msg []byte) {
   105  	if sq == nil {
   106  		return
   107  	}
   108  	out := outMsgPool.Get().(*outMsg)
   109  	out.subj, out.rply = subj, rply
   110  	out.hdr, out.msg = nil, nil
   111  
   112  	// We will copy these for now.
   113  	if len(hdr) > 0 {
   114  		hdr = copyBytes(hdr)
   115  		out.hdr = hdr
   116  	}
   117  	if len(msg) > 0 {
   118  		msg = copyBytes(msg)
   119  		out.msg = msg
   120  	}
   121  	sq.q.push(out)
   122  }