Skip to content

Commit

Permalink
Simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
SirOlaf committed Jul 22, 2024
1 parent c66bc48 commit 2d1b2c6
Showing 1 changed file with 57 additions and 92 deletions.
149 changes: 57 additions & 92 deletions lib/system/alloc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ type

MemRegion = object
chunksInUse: int
isAbandoned: bool
when not defined(gcDestructors):
minLargeObj, maxLargeObj: int
freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
Expand Down Expand Up @@ -876,7 +875,6 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
sysAssert(not a.isAbandoned, "rawAlloc: Abandoned allocator tried to allocate")

if size <= SmallChunkSize-smallChunkOverhead():
template fetchSharedCells(tc: PSmallChunk) =
Expand Down Expand Up @@ -1027,81 +1025,67 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
# ^ We might access thread foreign storage here.
# The other thread cannot possibly free this block as it's still alive.
var f = cast[ptr FreeCell](p)
if c.owner.isAbandoned:
if atomicAddFetch(addr c.free, s.int32, ATOMIC_RELAXED) == SmallChunkSize - smallChunkOverhead():
dec c.owner.chunksInUse
sysAssert(c.owner.chunksInUse >= 0, "rawDealloc: Abandoned allocator with negative chunks")
else:
if c.owner == addr(a):
# We own the block, there is no foreign thread involved.
dec a.occ, s
untrackSize(s)
sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 3")
when not defined(gcDestructors):
#echo("setting to nil: ", $cast[int](addr(f.zeroField)))
sysAssert(f.zeroField != 0, "rawDealloc 1")
f.zeroField = 0
when overwriteFree:
# set to 0xff to check for usage after free bugs:
nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
s -% sizeof(FreeCell))
let activeChunk = a.freeSmallChunks[s div MemAlign]
if activeChunk != nil and c != activeChunk:
# This pointer is not part of the active chunk, lend it out
# and do not adjust the current chunk (same logic as compensateCounters.)
# Put the cell into the active chunk,
# may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign].
# This queue would otherwise waste memory in the form of free cells until we return to those chunks.
f.next = activeChunk.freeList
activeChunk.freeList = f # lend the cell
inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed
inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk
else:
f.next = c.freeList
c.freeList = f
if c.free < s:
# The chunk could not have been active as it didn't have enough space to give
listAdd(a.freeSmallChunks[s div MemAlign], c)
inc(c.free, s)
else:
inc(c.free, s)
# Free only if the entire chunk is unused and there are no borrowed cells.
# If the chunk were to be freed while it references foreign cells,
# the foreign chunks will leak memory and can never be freed.
if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
listRemove(a.freeSmallChunks[s div MemAlign], c)
c.size = SmallChunkSize
freeBigChunk(a, cast[PBigChunk](c))
if c.owner == addr(a):
# We own the block, there is no foreign thread involved.
dec a.occ, s
untrackSize(s)
sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 3")
when not defined(gcDestructors):
#echo("setting to nil: ", $cast[int](addr(f.zeroField)))
sysAssert(f.zeroField != 0, "rawDealloc 1")
f.zeroField = 0
when overwriteFree:
# set to 0xff to check for usage after free bugs:
nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
s -% sizeof(FreeCell))
let activeChunk = a.freeSmallChunks[s div MemAlign]
if activeChunk != nil and c != activeChunk:
# This pointer is not part of the active chunk, lend it out
# and do not adjust the current chunk (same logic as compensateCounters.)
# Put the cell into the active chunk,
# may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign].
# This queue would otherwise waste memory in the form of free cells until we return to those chunks.
f.next = activeChunk.freeList
activeChunk.freeList = f # lend the cell
inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed
inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk
else:
when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
f.next = c.freeList
c.freeList = f
if c.free < s:
# The chunk could not have been active as it didn't have enough space to give
listAdd(a.freeSmallChunks[s div MemAlign], c)
inc(c.free, s)
else:
inc(c.free, s)
# Free only if the entire chunk is unused and there are no borrowed cells.
# If the chunk were to be freed while it references foreign cells,
# the foreign chunks will leak memory and can never be freed.
if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
listRemove(a.freeSmallChunks[s div MemAlign], c)
c.size = SmallChunkSize
freeBigChunk(a, cast[PBigChunk](c))
else:
when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))

when defined(gcDestructors):
addToSharedFreeList(c, f, s div MemAlign)
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 2")
when defined(gcDestructors):
addToSharedFreeList(c, f, s div MemAlign)
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 2")
else:
let c = cast[PBigChunk](c)
if c.owner.isAbandoned:
dec c.owner.chunksInUse
sysAssert(c.owner.chunksInUse >= 0, "rawDealloc: Abandoned allocator with negative chunks")
else:
# set to 0xff to check for usage after free bugs:
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
when defined(gcDestructors):
if c.owner == addr(a):
deallocBigChunk(a, c)
else:
addToSharedFreeListBigChunks(c.owner[], c)
else:
# set to 0xff to check for usage after free bugs:
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
when defined(gcDestructors):
if c.owner == addr(a):
deallocBigChunk(a, c)

if c.owner.isAbandoned:
if c.owner.chunksInUse == 0:
deallocOsPages(c.owner[])
osDeallocPages(p, sizeof(MemRegion))
else:
addToSharedFreeListBigChunks(c.owner[], c)
else:
deallocBigChunk(a, c)

sysAssert(allocInv(a), "rawDealloc: end")
#when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
Expand Down Expand Up @@ -1204,8 +1188,6 @@ proc dealloc(allocator: var MemRegion, p: pointer) =
rawDealloc(allocator, p)

proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
sysAssert(not allocator.isAbandoned, "realloc: Abandoned allocator")
#sysAssert(not pageAddr(p).owner.isabandoned, "realloc: Abandoned ptr")
if newsize > 0:
result = alloc(allocator, newsize)
if p != nil:
Expand All @@ -1221,24 +1203,9 @@ proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural):

proc abandonAllocator(a: var MemRegion) =
# There may be a race condition here...
a.isAbandoned = true
if a.chunksInUse == 0:
# Deallocating is only safe if all chunks are unused
deallocOsPages(a)
else:
var deadRegionPtr = cast[ptr MemRegion](a.tryAllocPages(sizeof(MemRegion)))
copyMem(deadRegionPtr, addr a, sizeof(MemRegion))

var it = addr a.heapLinks
while it != nil:
for i in 0 ..< it.len:
var off = 0
# Very dirty way of finding chunks
while off <= it.chunks[i][1] - sizeof(BaseChunk) - 8:
let maybeChunk = cast[ptr BaseChunk](cast[int](it.chunks[i][0]) + off)
if maybeChunk.owner == addr(a):
maybeChunk.owner = deadRegionPtr
inc(off)
it = it.next

proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
Expand Down Expand Up @@ -1267,8 +1234,6 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =

proc abandonAllocator = abandonAllocator(allocator)

proc dbgGetAllocatorAddr: uint = cast[uint](addr allocator)

proc allocImpl(size: Natural): pointer =
result = alloc(allocator, size)

Expand Down

0 comments on commit 2d1b2c6

Please sign in to comment.