diff --git a/internal/cutil/ptrguard_test.go b/internal/cutil/ptrguard_test.go new file mode 100644 index 0000000..b7e9042 --- /dev/null +++ b/internal/cutil/ptrguard_test.go @@ -0,0 +1,75 @@ +package cutil + +import ( + "math/rand" + "testing" + "unsafe" + + "github.com/stretchr/testify/assert" +) + +func TestPtrGuard(t *testing.T) { + t.Run("storeAndRelease", func(t *testing.T) { + s := "string" + goPtr := (unsafe.Pointer)(&s) + cPtr := Malloc(PtrSize) + defer Free(cPtr) + pg := NewPtrGuard(cPtr, goPtr) + assert.Equal(t, *(*unsafe.Pointer)(cPtr), goPtr) + pg.Release() + assert.Zero(t, *(*unsafe.Pointer)(cPtr)) + }) + + t.Run("multiRelease", func(t *testing.T) { + s := "string" + goPtr := (unsafe.Pointer)(&s) + cPtr := Malloc(PtrSize) + defer Free(cPtr) + pg := NewPtrGuard(cPtr, goPtr) + assert.Equal(t, *(*unsafe.Pointer)(cPtr), goPtr) + pg.Release() + pg.Release() + pg.Release() + pg.Release() + assert.Zero(t, *(*unsafe.Pointer)(cPtr)) + }) + + t.Run("stressTest", func(t *testing.T) { + // Because the default thread limit of the Go runtime is 10000, creating + // 20000 parallel PtrGuards asserts, that Go routines of PtrGuards don't + // create threads. + const N = 20000 // Number of parallel PtrGuards + const M = 100000 // Number of loops + var ptrGuards [N]*PtrGuard + cPtrArr := (*[N]CPtr)(unsafe.Pointer(Malloc(N * PtrSize))) + defer Free(CPtr(&cPtrArr[0])) + toggle := func(i int) { + if ptrGuards[i] == nil { + goPtr := unsafe.Pointer(&(struct{ byte }{42})) + cPtrPtr := CPtr(&cPtrArr[i]) + ptrGuards[i] = NewPtrGuard(cPtrPtr, goPtr) + assert.Equal(t, (unsafe.Pointer)(cPtrArr[i]), goPtr) + } else { + ptrGuards[i].Release() + ptrGuards[i] = nil + assert.Zero(t, cPtrArr[i]) + } + } + for i := range ptrGuards { + toggle(i) + } + for n := 0; n < M; n++ { + i := rand.Intn(N) + toggle(i) + } + for i := range ptrGuards { + if ptrGuards[i] != nil { + ptrGuards[i].Release() + ptrGuards[i] = nil + } + } + for i := uintptr(0); i < N; i++ { + assert.Zero(t, cPtrArr[i]) + } + }) +}