github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/link/sharedmem/pipe/rx.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this 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 pipe
    16  
    17  // Rx is the receive side of the shared memory ring buffer.
    18  type Rx struct {
    19  	p pipe
    20  
    21  	tail uint64
    22  	head uint64
    23  }
    24  
    25  // Init initializes the receive end of the pipe. In the initial state, the next
    26  // slot to be inspected is the very first one.
    27  func (r *Rx) Init(b []byte) {
    28  	r.p.init(b)
    29  	r.tail = 0xfffffffe * jump
    30  	r.head = r.tail
    31  }
    32  
    33  // Pull reads the next buffer from the pipe, returning nil if there isn't one
    34  // currently available.
    35  //
    36  // The returned slice is available until Flush() is next called. After that, it
    37  // must not be touched.
    38  func (r *Rx) Pull() []byte {
    39  	if r.head == r.tail+jump {
    40  		// We've already pulled the whole pipe.
    41  		return nil
    42  	}
    43  
    44  	header := r.p.readAtomic(r.head)
    45  	if header&slotFree != 0 {
    46  		// The next slot is free, we can't pull it yet.
    47  		return nil
    48  	}
    49  
    50  	payloadSize := header & slotSizeMask
    51  	newHead := r.head + payloadToSlotSize(payloadSize)
    52  	headWrap := (r.head & revolutionMask) | uint64(len(r.p.buffer))
    53  
    54  	// Check if this is a wrapping slot. If that's the case, it carries no
    55  	// data, so we just skip it and try again from the first slot.
    56  	if int64(newHead-headWrap) >= 0 {
    57  		// If newHead passes the tail, the pipe is either damaged or the
    58  		// RX view of the pipe has completely wrapped without an
    59  		// intervening flush.
    60  		if int64(newHead-(r.tail+jump)) > 0 {
    61  			return nil
    62  		}
    63  		// The pipe is damaged if newHead doesn't point to the start of
    64  		// the ring.
    65  		if newHead&offsetMask != 0 {
    66  			return nil
    67  		}
    68  
    69  		if r.tail == r.head {
    70  			// If this is the first pull since the last Flush()
    71  			// call, we flush the state so that the sender can use
    72  			// this space if it needs to.
    73  			r.p.writeAtomic(r.head, slotFree|slotToPayloadSize(newHead-r.head))
    74  			r.tail = newHead
    75  		}
    76  
    77  		r.head = newHead
    78  		return r.Pull()
    79  	}
    80  
    81  	// Grab the buffer before updating r.head.
    82  	b := r.p.data(r.head, payloadSize)
    83  	r.head = newHead
    84  	return b
    85  }
    86  
    87  // Flush tells the transmitter that all buffers pulled since the last Flush()
    88  // have been used, so the transmitter is free to used their slots for further
    89  // transmission.
    90  func (r *Rx) Flush() {
    91  	if r.head == r.tail {
    92  		return
    93  	}
    94  	r.p.writeAtomic(r.tail, slotFree|slotToPayloadSize(r.head-r.tail))
    95  	r.tail = r.head
    96  }
    97  
    98  // Abort unpulls any pulled buffers.
    99  func (r *Rx) Abort() {
   100  	r.head = r.tail
   101  }
   102  
   103  // Bytes returns the byte slice on which the pipe operates.
   104  func (r *Rx) Bytes() []byte {
   105  	return r.p.buffer
   106  }