lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/mem/buffer_test.go (about)

     1  // Copyright (C) 2017  Nexedi SA and Contributors.
     2  //                     Kirill Smelkov <kirr@nexedi.com>
     3  //
     4  // This program is free software: you can Use, Study, Modify and Redistribute
     5  // it under the terms of the GNU General Public License version 3, or (at your
     6  // option) any later version, as published by the Free Software Foundation.
     7  //
     8  // You can also Link and Combine this program with other software covered by
     9  // the terms of any of the Free Software licenses or any of the Open Source
    10  // Initiative approved licenses and Convey the resulting work. Corresponding
    11  // source of such a combination shall include the source code for all other
    12  // software used.
    13  //
    14  // This program is distributed WITHOUT ANY WARRANTY; without even the implied
    15  // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    16  //
    17  // See COPYING file for full licensing terms.
    18  // See https://www.nexedi.com/licensing for rationale and options.
    19  
    20  // As of go19 sync.Pool under race-detector randomly drops items on the floor
    21  // https://github.com/golang/go/blob/ca360c39/src/sync/pool.go#L92
    22  // so it is not possible to verify we will get what we've just put there.
    23  //go:build !race
    24  // +build !race
    25  
    26  package mem
    27  
    28  import (
    29  	"reflect"
    30  	"testing"
    31  	"unsafe"
    32  )
    33  
    34  //go:linkname runtime_procPin runtime.procPin
    35  //go:linkname runtime_procUnpin runtime.procUnpin
    36  func runtime_procPin() int
    37  func runtime_procUnpin()
    38  
    39  
    40  func sliceDataPtr(b []byte) unsafe.Pointer {
    41  	return unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
    42  }
    43  
    44  func TestBufAllocFree(t *testing.T) {
    45  	// sync.Pool uses per-P free-lists. We check that after release we will
    46  	// allocate released object. This works only if P is not changed underneath.
    47  	runtime_procPin()
    48  	defer runtime_procUnpin()
    49  
    50  	for i := uint(0); i < 25; i++ {
    51  		size := 1<<i - 1
    52  		xcap := 1<<i
    53  		buf := BufAlloc(size)
    54  		if i < order0 {
    55  			xcap = 1 << order0
    56  		}
    57  		if int(i) >= order0+len(bufPoolv) {
    58  			xcap = size
    59  		}
    60  
    61  		if len(buf.Data) != size {
    62  			t.Fatalf("%v: len=%v  ; want %v", i, len(buf.Data), size)
    63  		}
    64  		if cap(buf.Data) != xcap {
    65  			t.Fatalf("%v: cap=%v  ; want %v", i, cap(buf.Data), xcap)
    66  		}
    67  
    68  		checkref := func(rc int32) {
    69  			t.Helper()
    70  			if buf.refcnt != rc {
    71  				t.Fatalf("%v: refcnt=%v  ; want %v", i, buf.refcnt, rc)
    72  			}
    73  		}
    74  
    75  		checkref(0)
    76  
    77  		// free and allocate another buf -> it must be it
    78  		data := buf.Data
    79  		buf.Release()
    80  		checkref(-1)
    81  		buf2 := BufAlloc(size)
    82  
    83  		// not from pool - memory won't be reused
    84  		if int(i) >= order0+len(bufPoolv) {
    85  			if buf2 == buf || sliceDataPtr(buf2.Data) == sliceDataPtr(data) {
    86  				t.Fatalf("%v: buffer reused but should not", i)
    87  			}
    88  			continue
    89  		}
    90  
    91  		// from pool -> it must be the same
    92  		if !(buf2 == buf && sliceDataPtr(buf2.Data) == sliceDataPtr(data)) {
    93  			t.Fatalf("%v: buffer not reused on free/realloc", i)
    94  		}
    95  		checkref(0)
    96  
    97  		// add more ref and release original buf - it must stay alive
    98  		buf.Incref()
    99  		checkref(1)
   100  		buf.Release()
   101  		checkref(0)
   102  
   103  		// another alloc must be different
   104  		buf2 = BufAlloc(size)
   105  		checkref(0)
   106  
   107  		if buf2 == buf || sliceDataPtr(buf2.Data) == sliceDataPtr(data) {
   108  			t.Fatalf("%v: buffer reused but should not", i)
   109  		}
   110  
   111  		// release buf again -> should go to pool
   112  		buf.Release()
   113  		checkref(-1)
   114  		buf2 = BufAlloc(size)
   115  		if !(buf2 == buf && sliceDataPtr(buf2.Data) == sliceDataPtr(data)) {
   116  			t.Fatalf("%v: buffer not reused on free/realloc", i)
   117  		}
   118  		checkref(0)
   119  	}
   120  }