summary refs log tree commit diff stats
path: root/lib/memman.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/memman.nim')
-rw-r--r--lib/memman.nim217
1 files changed, 155 insertions, 62 deletions
diff --git a/lib/memman.nim b/lib/memman.nim
index f0ca078f7..81919389b 100644
--- a/lib/memman.nim
+++ b/lib/memman.nim
@@ -37,24 +37,24 @@ const
   realFli = MaxFli - fliOffset
   
 type
-  TFreePtr = record
+  TFreePtr {.final.} = object
     prev, next: PBhdr
   Pbhdr = ptr Tbhdr
-  Tbhdr = record
+  Tbhdr {.final.} = object
     prevHdr: Pbhdr # this is just valid if the first bit of size is set
     size: int # the size is stored in bytes 
               # bit 0 indicates whether the block is used and
               # bit 1 allows to know whether the previous block is free
     freePtr: TFreePtr # at this offset bhdr.buffer starts (was a union in the
                       # C version)
-  TAreaInfo = record  # This structure is embedded at the beginning of each
-                      # area, giving us enough information to cope with a set
-                      # of areas
+  TAreaInfo  {.final.} = object  # This structure is embedded at the beginning
+                                 # of each area, giving us enough information 
+                                 # to cope with a set of areas
     theEnd: Pbhdr
     next: PAreaInfo
   PAreaInfo = ptr TAreaInfo
 
-  TLSF = record    
+  TLSF {.final.} = object
     tlsf_signature: int32 # the TLSF's structure signature
     usedSize, maxSize: int
     areaHead: PAreaInfo # A linked list holding all the existing areas
@@ -147,6 +147,7 @@ elif defined(windows):
     if result == nil: raiseOutOfMem()
 
 else: 
+  {.warning: "Generic code for allocating pages is used".}
   # generic implementation relying on malloc:
   proc malloc(size: int): pointer {.nodecl, importc.}
   
@@ -155,6 +156,124 @@ else:
     result = malloc(size)
     if result == nil: raiseOutOfMem()
 
+# ---------------------------------------------------------------------------
+# small fixed size allocator:
+
+# Design: We manage pages. A page is of constant size, but not necessarily
+# the OS's page size. Pages are managed in a hash table taking advantage of
+# the fact that the OS is likely to give us pages with contingous numbers. 
+# A page contains either small fixed size objects of the same size or 
+# variable length objects. An object's size is always aligned at 16 byte
+# boundry. Huge objects are dealt with the TLSF algorithm. 
+# The design supports iterating over any object in a fast way. 
+
+# A bitset contains any page that starts an allocated page. The page may be
+# empty however. This bitset can be used to quickly determine if a given
+# page belongs to the GC heap. The design of the memory allocator makes it 
+# simple to return unused pages back to the OS.
+
+
+# Small bocks 
+# -----------
+#
+# If we use a list in the free object's space. Allocation and deallocation are
+# O(1). Since any object is of the same size, iteration is quite efficient too.
+# However, pointer detection is easy too: Just check if the type-field is nil.
+# Deallocation sets it to nil.
+# Algorithm:
+
+# i = 0
+# f = b.f # first free address
+# while i < max: 
+#  if a[i] == f: # not a valid block
+#    f = f.next  # next free address
+#  else:
+#    a[i] is a valid object of size s
+#  inc(i)
+
+# The zero count table is an array. Since we know that the RC is zero, we can
+# use the bits for an index into this array. Thus multiple ZCT tables are not
+# difficult to support and insertion and removal is O(1). We use negative
+# indexes for this. This makes it even fast enough (and necessary!) to do a ZCT
+# removal if the RC is incremented. 
+# 
+
+# Huge blocks
+# -----------
+#
+# Huge blocks are always rounded up to a multiple of the page size. These are
+# called *strides*. We also need to keep an index structure 
+# of (stridesize, pagelist). 
+# 
+
+const
+  MemAlign = 8
+  PageShift = if sizeof(int) == 4: 12 else: 13
+  PageSize = 1 shl PageShift
+type
+  TFreeList {.final.} = object
+    next, prev: ptr TFreeList
+
+  TPageDesc {.final.} = object  # the start of a stride always starts with this!
+    size: int                   # lowest bit is set, if it is a huge block
+    free: ptr TFreeList         # only used if it manages multiple cells
+    snext, sprev: ptr TPageDesc # next and prev pagedescs with the same size
+
+  TCellArray {.final.} = object
+    i: int # length
+    d: ptr array [0..1000_000, TCell]
+
+  TPageManager = table[page, ptr TPageDesc]
+
+  TGcHeap {.final.} = object
+    # structure that manages the garbage collected heap
+    zct: TCellArray
+    stackCells: TCellArray
+    smallBlocks: array [PageSize div MemAlign, ptr TPageDesc]
+    pages: TPageManager
+    usedPages: TPageList
+    freePages: TPageList
+
+# small blocks: 
+proc allocSmall(var h: TGcHeap, size: int): pointer = 
+  var s = align(size)
+  var p = h.smallBlocks[s]
+  if p == nil or p.free == nil:
+    p = newSmallBlock(s, p)
+    h.smallBlocks[s] = p
+  
+
+
+proc decRef(cell: PCell) {.inline.} =
+  assert(cell in ct.AT)
+  assert(cell.refcount > 0) # this should be the case!
+  assert(seqCheck(cell))
+  dec(cell.refcount)
+  if cell.refcount == 0:
+    # add to zero count table:
+    zct.d[zct.i] = cell
+    cell.recfcount = -zct.i
+    inc(zct.i)
+
+proc incRef(cell: PCell) {.inline.} =
+  assert(seqCheck(cell))
+  if cell.refcount < 0: 
+    # remove from zero count table:
+    zct.d[-cell.refcount] = zct.d[zct.i-1]
+    dec(zct.i) 
+    cell.refcount = 1
+  else:
+    inc(cell.refcount)
+
+proc asgnRef(dest: ppointer, src: pointer) =
+  # the code generator calls this proc!
+  assert(not isOnStack(dest))
+  # BUGFIX: first incRef then decRef!
+  if src != nil: incRef(usrToCell(src))
+  if dest^ != nil: decRef(usrToCell(dest^))
+  dest^ = src
+
+
 # ----------------------------------------------------------------------------
 #     helpers
 
@@ -261,7 +380,7 @@ proc extractBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
 proc insertBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} = 
   b.freePtr.prev = nil
   b.freePtr.next = t.matrix[fl][sl] 
-  if t.matrix[fl][sl] != 0:
+  if t.matrix[fl][sl] != nil:
     t.matrix[fl][sl].freePtr.prev = b		
   t.matrix[fl][sl] = b
   set_bit(sl, t.slBitmap[fl])
@@ -295,9 +414,6 @@ proc processArea(area: pointer, size: int): Pbhdr =
 # ----------------------------------------------------------------------------
 #                  Begin of the allocator code
 
-var
-  mp: pointer # default memory pool.
-
 proc initMemoryPool(memPoolSize: int, memPool: pointer): int = 
   var
     t: PLSF
@@ -307,16 +423,14 @@ proc initMemoryPool(memPoolSize: int, memPool: pointer): int =
     writeToStdErr("initMemoryPool(): memory_pool invalid\n")
     return -1
 
-  if (memPool and ptrMask) != 0:
+  if (cast[TAddress](memPool) and ptrMask) != 0:
     writeToStdErr("initMemoryPool(): memPool must be aligned to a word\n")
     return -1
   t = cast[PLSF](memPool)
   # Check if already initialised
   if t.signature == tlsfSignature:
-    mp = memPool
-    b = getNextBlock(mp, roundupSize(sizeof(TLSF)))
+    b = getNextBlock(memPool, roundupSize(sizeof(TLSF)))
     return b.size and blockSize
-  mp = memPool
   zeroMem(memPool, sizeof(TLSF))
 
   t.signature = tlsfSignature
@@ -456,7 +570,7 @@ proc freeEx(p: pointer, t: var TLSF) =
   b.freePtr.prev = nil
   b.freePtr.next = nil
   tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
-  if tmpB.size and freeBlock != 0:
+  if (tmpB.size and freeBlock) != 0:
     mappingInsert(tmpB.size and blockSize, fl, sl)
     extractBlock(tmpB, t, fl, sl)
     inc(b.size, (tmpB.size and blockSize) + bhdrOverhead)
@@ -549,51 +663,30 @@ proc ansiCrealloc(p: pointer, newSize: int, t: var TLSF): pointer =
   else:
     result = reallocEx(p, newSize, t)
 
+proc InitTLSF(t: var TLSF) = 
+  var areaSize = sizeof(TLSF) + BHDR_OVERHEAD * 8 # Just a safety constant
+  areaSize = max(areaSize, DEFAULT_areaSize)
+  var area = getNewArea(areaSize)
+  
+  
+  initMemoryPool(areaSize, area)
+
+  var
+    t: PLSF
+    b, ib: Pbhdr
+
+  t = cast[PLSF](memPool)
+  
+  zeroMem(area, areaSize)
+
+  t.signature = tlsfSignature
+  var ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))), 
+                   rounddownSize(memPoolSize - sizeof(TLSF)))
+  var b = getNextBlock(getBuffer(ib), ib.size and blockSize)
+  freeEx(getBuffer(b), t)
+  t.areaHead = cast[PAreaInfo](getBuffer(ib))
+
+  t.used_size = memPoolSize - (b.size and blockSize)
+  t.max_size = t.used_size
 
-void *tlsf_malloc(size_t size)
-{
-  void *ret
-
-#if USE_MMAP || USE_SBRK
-  if (!mp) {
-    size_t areaSize
-    void *area
-
-    areaSize = sizeof(tlsf_t) + BHDR_OVERHEAD * 8 # Just a safety constant
-    areaSize = (areaSize > DEFAULT_areaSize) ? areaSize : DEFAULT_areaSize
-    area = get_new_area(&areaSize)
-    if (area == ((void *) ~0))
-      return NULL        # Not enough system memory 
-    initMemoryPool(areaSize, area)
-  }
-#endif
-
-  TLSF_ACQUIRE_LOCK(&((tlsf_t *)mp)->lock)
-
-  ret = malloc_ex(size, mp)
-
-  TLSF_RELEASE_LOCK(&((tlsf_t *)mp)->lock)
-
-  return ret
-}
-
-void tlsf_free(void *p)
-{
-  TLSF_ACQUIRE_LOCK(&((tlsf_t *)mp)->lock)
-  free_ex(p, mp)
-  TLSF_RELEASE_LOCK(&((tlsf_t *)mp)->lock)
-}
-
-void *tlsf_realloc(void *p, size_t size)
-{
-  void *ret
-#if USE_MMAP || USE_SBRK
-  if (!mp) {
-    return tlsf_malloc(size)
-  }
-#endif
-  TLSF_ACQUIRE_LOCK(&((tlsf_t *)mp)->lock)
-  ret = realloc_ex(p, size, mp)
-  TLSF_RELEASE_LOCK(&((tlsf_t *)mp)->lock)
-  return ret
-}
+  # XXX