google.golang.org/grpc@v1.72.2/mem/buffers_test.go (about) 1 /* 2 * 3 * Copyright 2024 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package mem_test 20 21 import ( 22 "bytes" 23 "testing" 24 25 "google.golang.org/grpc/internal" 26 "google.golang.org/grpc/internal/grpctest" 27 "google.golang.org/grpc/mem" 28 ) 29 30 type s struct { 31 grpctest.Tester 32 } 33 34 func Test(t *testing.T) { 35 internal.SetBufferPoolingThresholdForTesting.(func(int))(0) 36 37 grpctest.RunSubTests(t, s{}) 38 } 39 40 // Tests that a buffer created with NewBuffer, which when later freed, invokes 41 // the free function with the correct data. 42 func (s) TestBuffer_NewBufferAndFree(t *testing.T) { 43 data := "abcd" 44 freed := false 45 freeF := poolFunc(func(got *[]byte) { 46 if !bytes.Equal(*got, []byte(data)) { 47 t.Fatalf("Free function called with bytes %s, want %s", string(*got), data) 48 } 49 freed = true 50 }) 51 52 buf := newBuffer([]byte(data), freeF) 53 if got := buf.ReadOnlyData(); !bytes.Equal(got, []byte(data)) { 54 t.Fatalf("Buffer contains data %s, want %s", string(got), string(data)) 55 } 56 57 // Verify that the free function is invoked when all references are freed. 58 buf.Free() 59 if !freed { 60 t.Fatalf("Buffer not freed") 61 } 62 } 63 64 // Tests that a buffer created with NewBuffer, on which an additional reference 65 // is acquired, which when later freed, invokes the free function with the 66 // correct data, but only after all references are released. 67 func (s) TestBuffer_NewBufferRefAndFree(t *testing.T) { 68 data := "abcd" 69 freed := false 70 freeF := poolFunc(func(got *[]byte) { 71 if !bytes.Equal(*got, []byte(data)) { 72 t.Fatalf("Free function called with bytes %s, want %s", string(*got), string(data)) 73 } 74 freed = true 75 }) 76 77 buf := newBuffer([]byte(data), freeF) 78 if got := buf.ReadOnlyData(); !bytes.Equal(got, []byte(data)) { 79 t.Fatalf("Buffer contains data %s, want %s", string(got), string(data)) 80 } 81 82 buf.Ref() 83 if got := buf.ReadOnlyData(); !bytes.Equal(got, []byte(data)) { 84 t.Fatalf("New reference to the Buffer contains data %s, want %s", string(got), string(data)) 85 } 86 87 // Verify that the free function is not invoked when all references are yet 88 // to be freed. 89 buf.Free() 90 if freed { 91 t.Fatalf("Free function called before all references freed") 92 } 93 94 // Verify that the free function is invoked when all references are freed. 95 buf.Free() 96 if !freed { 97 t.Fatalf("Buffer not freed") 98 } 99 } 100 101 func (s) TestBuffer_NewBufferHandlesShortBuffers(t *testing.T) { 102 const threshold = 100 103 104 // Update the pooling threshold, since that's what's being tested. 105 internal.SetBufferPoolingThresholdForTesting.(func(int))(threshold) 106 t.Cleanup(func() { 107 internal.SetBufferPoolingThresholdForTesting.(func(int))(0) 108 }) 109 110 // Make a pool with a buffer whose capacity is larger than the pooling 111 // threshold, but whose length is less than the threshold. 112 b := make([]byte, threshold/2, threshold*2) 113 pool := &singleBufferPool{ 114 t: t, 115 data: &b, 116 } 117 118 // Get a Buffer, then free it. If NewBuffer decided that the Buffer 119 // shouldn't get pooled, Free will be a noop and singleBufferPool will not 120 // have been updated. 121 mem.NewBuffer(&b, pool).Free() 122 123 if pool.data != nil { 124 t.Fatalf("Buffer not returned to pool") 125 } 126 } 127 128 func (s) TestBuffer_FreeAfterFree(t *testing.T) { 129 buf := newBuffer([]byte("abcd"), mem.NopBufferPool{}) 130 if buf.Len() != 4 { 131 t.Fatalf("Buffer length is %d, want 4", buf.Len()) 132 } 133 134 // Ensure that a double free does panic. 135 buf.Free() 136 defer checkForPanic(t, "Cannot free freed buffer") 137 buf.Free() 138 } 139 140 type singleBufferPool struct { 141 t *testing.T 142 data *[]byte 143 } 144 145 func (s *singleBufferPool) Get(length int) *[]byte { 146 if len(*s.data) != length { 147 s.t.Fatalf("Invalid requested length, got %d want %d", length, len(*s.data)) 148 } 149 return s.data 150 } 151 152 func (s *singleBufferPool) Put(b *[]byte) { 153 if s.data != b { 154 s.t.Fatalf("Wrong buffer returned to pool, got %p want %p", b, s.data) 155 } 156 s.data = nil 157 } 158 159 // Tests that a buffer created with Copy, which when later freed, returns the underlying 160 // byte slice to the buffer pool. 161 func (s) TestBuffer_CopyAndFree(t *testing.T) { 162 data := []byte("abcd") 163 testPool := &singleBufferPool{ 164 t: t, 165 data: &data, 166 } 167 168 buf := mem.Copy(data, testPool) 169 if got := buf.ReadOnlyData(); !bytes.Equal(got, data) { 170 t.Fatalf("Buffer contains data %s, want %s", string(got), string(data)) 171 } 172 173 // Verify that the free function is invoked when all references are freed. 174 buf.Free() 175 if testPool.data != nil { 176 t.Fatalf("Buffer not freed") 177 } 178 } 179 180 // Tests that a buffer created with Copy, on which an additional reference is 181 // acquired, which when later freed, returns the underlying byte slice to the 182 // buffer pool. 183 func (s) TestBuffer_CopyRefAndFree(t *testing.T) { 184 data := []byte("abcd") 185 testPool := &singleBufferPool{ 186 t: t, 187 data: &data, 188 } 189 190 buf := mem.Copy(data, testPool) 191 if got := buf.ReadOnlyData(); !bytes.Equal(got, data) { 192 t.Fatalf("Buffer contains data %s, want %s", string(got), string(data)) 193 } 194 195 buf.Ref() 196 if got := buf.ReadOnlyData(); !bytes.Equal(got, []byte(data)) { 197 t.Fatalf("New reference to the Buffer contains data %s, want %s", string(got), string(data)) 198 } 199 200 // Verify that the free function is not invoked when all references are yet 201 // to be freed. 202 buf.Free() 203 if testPool.data == nil { 204 t.Fatalf("Free function called before all references freed") 205 } 206 207 // Verify that the free function is invoked when all references are freed. 208 buf.Free() 209 if testPool.data != nil { 210 t.Fatalf("Free never called") 211 } 212 } 213 214 func (s) TestBuffer_ReadOnlyDataAfterFree(t *testing.T) { 215 // Verify that reading before freeing does not panic. 216 buf := newBuffer([]byte("abcd"), mem.NopBufferPool{}) 217 buf.ReadOnlyData() 218 219 buf.Free() 220 defer checkForPanic(t, "Cannot read freed buffer") 221 buf.ReadOnlyData() 222 } 223 224 func (s) TestBuffer_RefAfterFree(t *testing.T) { 225 // Verify that acquiring a ref before freeing does not panic. 226 buf := newBuffer([]byte("abcd"), mem.NopBufferPool{}) 227 buf.Ref() 228 229 // This first call should not panic and bring the ref counter down to 1 230 buf.Free() 231 // This second call actually frees the buffer 232 buf.Free() 233 defer checkForPanic(t, "Cannot ref freed buffer") 234 buf.Ref() 235 } 236 237 func (s) TestBuffer_SplitAfterFree(t *testing.T) { 238 // Verify that splitting before freeing does not panic. 239 buf := newBuffer([]byte("abcd"), mem.NopBufferPool{}) 240 buf, bufSplit := mem.SplitUnsafe(buf, 2) 241 242 bufSplit.Free() 243 buf.Free() 244 defer checkForPanic(t, "Cannot split freed buffer") 245 mem.SplitUnsafe(buf, 2) 246 } 247 248 type poolFunc func(*[]byte) 249 250 func (p poolFunc) Get(length int) *[]byte { 251 panic("Get should never be called") 252 } 253 254 func (p poolFunc) Put(i *[]byte) { 255 p(i) 256 } 257 258 func (s) TestBuffer_Split(t *testing.T) { 259 ready := false 260 freed := false 261 data := []byte{1, 2, 3, 4} 262 buf := mem.NewBuffer(&data, poolFunc(func(bytes *[]byte) { 263 if !ready { 264 t.Fatalf("Freed too early") 265 } 266 freed = true 267 })) 268 checkBufData := func(b mem.Buffer, expected []byte) { 269 t.Helper() 270 if !bytes.Equal(b.ReadOnlyData(), expected) { 271 t.Fatalf("Buffer did not contain expected data %v, got %v", expected, b.ReadOnlyData()) 272 } 273 } 274 275 buf, split1 := mem.SplitUnsafe(buf, 2) 276 checkBufData(buf, data[:2]) 277 checkBufData(split1, data[2:]) 278 279 // Check that splitting the buffer more than once works as intended. 280 split1, split2 := mem.SplitUnsafe(split1, 1) 281 checkBufData(split1, data[2:3]) 282 checkBufData(split2, data[3:]) 283 284 // If any of the following frees actually free the buffer, the test will fail. 285 buf.Free() 286 split2.Free() 287 288 ready = true 289 split1.Free() 290 291 if !freed { 292 t.Fatalf("Buffer never freed") 293 } 294 } 295 296 func checkForPanic(t *testing.T, wantErr string) { 297 t.Helper() 298 r := recover() 299 if r == nil { 300 t.Fatalf("Use after free did not panic") 301 } 302 if msg, ok := r.(string); !ok || msg != wantErr { 303 t.Fatalf("panic called with %v, want %s", r, wantErr) 304 } 305 }