gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 }