Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6937fa5
Moved the generation tag to data-pointer
fmstephe Feb 2, 2025
4e41d47
Rename dataAddress to dataAddressAndGen
fmstephe Feb 2, 2025
ecc2da8
metadata now has dataAddressAndGen field
fmstephe Feb 2, 2025
441c231
NewReference panics if nil metaAddress is provided
fmstephe Feb 2, 2025
cf1e213
Add taggedAddress type for RefPointer
fmstephe Feb 2, 2025
8dde236
checkReference() checks only the raw value
fmstephe Feb 2, 2025
880f6b3
checkReference produces specific panic messages
fmstephe Feb 3, 2025
45fada8
Move metadata into its own file.
fmstephe Feb 3, 2025
8707593
Replace linked-list of free objects with slice
fmstephe Feb 3, 2025
4728a18
Implement stack for free list
fmstephe Feb 3, 2025
13efba9
Incrementing generation tag on free and realloc
fmstephe Feb 4, 2025
6a65b77
Add isFree bit to taggedAddress
fmstephe Feb 4, 2025
9263522
metadata now uses is-free bit in taggedAddress
fmstephe Feb 4, 2025
efa96fd
tidy-up of the taggedAddress constants
fmstephe Feb 4, 2025
de910e4
Add comment to checkReference() method
fmstephe Feb 4, 2025
a884022
Renamed dataAddressAndGen to address in RefPointer
fmstephe Feb 4, 2025
1e5bd8b
Fix metadata checks for RefPointer.Bytes(size)
fmstephe Feb 4, 2025
14d2b2f
RefPointer _only_ contains address of metadata
fmstephe Feb 4, 2025
4bfa75a
Use nilPtr constant in RefPointer constructor
fmstephe Feb 4, 2025
cc3e6c3
Check RefPointer is valid on Realloc
fmstephe Feb 4, 2025
214ed7f
Check if allocation is free in allocFromFree()
fmstephe Feb 4, 2025
2a2fbf8
Rename dataAddress() -> accessibleActiveAddress()
fmstephe Feb 4, 2025
fe5506d
RefPointer.allocFromFree() creates new RefPointer
fmstephe Feb 4, 2025
e813ac5
Remove pointer receivers from RefPointer methods
fmstephe Feb 4, 2025
cd137ce
Add dirty benchmark
fmstephe Feb 4, 2025
c128b4f
Update metadata comments
fmstephe Feb 6, 2025
d9e16a7
Remove RefPointer.metaAddresss()
fmstephe Feb 7, 2025
3ea7e5f
taggedAddress.address() -> pointer()
fmstephe Feb 7, 2025
33a2f22
Adding taggedAddress.withIncGen() method
fmstephe Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions offheap/internal/pointerstore/free_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2025 Francis Michael Stephens. All rights reserved. Use of this
// source code is governed by an MIT license that can be found in the LICENSE
// file.

package pointerstore

type freeStack struct {
free []RefPointer
}

func (s *freeStack) push(r RefPointer) {
s.free = append(s.free, r)
}

func (s *freeStack) pop() (r RefPointer, ok bool) {
l := len(s.free)
if l == 0 {
return RefPointer{}, false
}

r = s.free[l-1]
s.free = s.free[:l-1]
return r, true
}
95 changes: 95 additions & 0 deletions offheap/internal/pointerstore/free_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2025 Francis Michael Stephens. All rights reserved. Use of this
// source code is governed by an MIT license that can be found in the LICENSE
// file.

package pointerstore

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestFreeStack_Empty(t *testing.T) {
s := freeStack{}
r, ok := s.pop()
assert.True(t, r.IsNil())
assert.False(t, ok)
}

// Push 3 references onto the stack. Pop them off in reverse order
func TestFreeStack_PushPop(t *testing.T) {
allocConfig := NewAllocConfigBySize(8, 32*8)
objects, metadatas := MmapSlab(allocConfig)

s := freeStack{}

r1Push := NewReference(objects[0], metadatas[0])
r2Push := NewReference(objects[1], metadatas[1])
r3Push := NewReference(objects[2], metadatas[2])

s.push(r1Push)
s.push(r2Push)
s.push(r3Push)

r3Pop, ok3 := s.pop()
assert.Equal(t, r3Push, r3Pop)
assert.True(t, ok3)

r2Pop, ok2 := s.pop()
assert.Equal(t, r2Push, r2Pop)
assert.True(t, ok2)

r1Pop, ok1 := s.pop()
assert.Equal(t, r1Push, r1Pop)
assert.True(t, ok1)

r0Pop, ok0 := s.pop()
assert.True(t, r0Pop.IsNil())
assert.False(t, ok0)
}

// Push three references off, pop two, push two more. Pop remaining references.
func TestFreeStack_PushPopComplex(t *testing.T) {
allocConfig := NewAllocConfigBySize(8, 32*8)
objects, metadatas := MmapSlab(allocConfig)

s := freeStack{}

r1Push := NewReference(objects[0], metadatas[0])
r2Push := NewReference(objects[1], metadatas[1])
r3Push := NewReference(objects[2], metadatas[2])
r4Push := NewReference(objects[3], metadatas[3])
r5Push := NewReference(objects[4], metadatas[4])

s.push(r1Push)
s.push(r2Push)
s.push(r3Push)

r3Pop, ok3 := s.pop()
assert.Equal(t, r3Push, r3Pop)
assert.True(t, ok3)

r2Pop, ok2 := s.pop()
assert.Equal(t, r2Push, r2Pop)
assert.True(t, ok2)

s.push(r4Push)
s.push(r5Push)

r5Pop, ok5 := s.pop()
assert.Equal(t, r5Push, r5Pop)
assert.True(t, ok5)

r4Pop, ok4 := s.pop()
assert.Equal(t, r4Push, r4Pop)
assert.True(t, ok4)

r1Pop, ok1 := s.pop()
assert.Equal(t, r1Push, r1Pop)
assert.True(t, ok1)

r0Pop, ok0 := s.pop()
assert.True(t, r0Pop.IsNil())
assert.False(t, ok0)
}
43 changes: 43 additions & 0 deletions offheap/internal/pointerstore/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2025 Francis Michael Stephens. All rights reserved. Use of this
// source code is governed by an MIT license that can be found in the LICENSE
// file.

package pointerstore

// A RefPointer smuggles a generation tag. Only references with the same gen
// value can access/free objects they point to. This is a best-effort safety
// check to try to catch use-after-free type errors.
//
// Metadata smuggles a generation tag and an is-free tag into its
// taggedAddress. When accessing the data from a RefPointer we check that the
// metadata indicates the object is not free and that the RefPointer's
// generation matches that of the metadat.
type metadata struct {
dataAddressAndGen taggedAddress
}

//gcassert:noescape
func (m *metadata) gen() uint8 {
return m.dataAddressAndGen.gen()
}

//gcassert:noescape
func (m *metadata) incGen() uint8 {
m.dataAddressAndGen = m.dataAddressAndGen.withIncGen()
return m.dataAddressAndGen.gen()
}

//gcassert:noescape
func (m *metadata) isFree() bool {
return m.dataAddressAndGen.isFree()
}

//gcassert:noescape
func (m *metadata) setFree() {
m.dataAddressAndGen = m.dataAddressAndGen.withFree()
}

//gcassert:noescape
func (m *metadata) setNotFree() {
m.dataAddressAndGen = m.dataAddressAndGen.withNotFree()
}
Loading