gpgscm: Remove arbitrary limit on number of cell segments.
authorJustus Winter <justus@g10code.com>
Wed, 22 Mar 2017 15:22:57 +0000 (16:22 +0100)
committerJustus Winter <justus@g10code.com>
Fri, 7 Apr 2017 11:11:30 +0000 (13:11 +0200)
* tests/gpgscm/scheme-private.h (struct scheme): Remove fixed-size
arrays for cell segments, replace them with a pointer to the new
'struct cell_segment' instead.
* tests/gpgscm/scheme.c (struct cell_segment): New definition.
(_alloc_cellseg): Allocate the header within the segment, return a
pointer to the header.
(_dealloc_cellseg): New function.
(alloc_cellseg): Insert the segments into a list.
(_get_cell): Allocate a new segment if less than a quarter of
CELL_SIGSIZE is recovered during garbage collection.
(initialize_small_integers): Adapt callsite.
(gc): Walk the list of segments.
(scheme_init_custom_alloc): Remove initialization of removed field.
(scheme_deinit): Adapt deallocation.
--

Previously the number of cells that could be allocated was a
compile-time limit.  Remove this limit.

Signed-off-by: Justus Winter <justus@g10code.com>
tests/gpgscm/scheme-private.h
tests/gpgscm/scheme.c

index fe50135..093442f 100644 (file)
@@ -108,12 +108,7 @@ int tracing;
 #ifndef CELL_SEGSIZE
 #define CELL_SEGSIZE    5000  /* # of cells in one segment */
 #endif
-#ifndef CELL_NSEGMENT
-#define CELL_NSEGMENT   10    /* # of segments for cells */
-#endif
-void *alloc_seg[CELL_NSEGMENT];
-pointer cell_seg[CELL_NSEGMENT];
-int     last_cell_seg;
+struct cell_segment *cell_segments;
 
 /* We use 4 registers. */
 pointer args;            /* register for arguments of function */
@@ -159,8 +154,7 @@ pointer COMPILE_HOOK;  /* *compile-hook* */
 
 #if USE_SMALL_INTEGERS
 /* A fixed allocation of small integers.  */
-void *integer_alloc;
-pointer integer_cells;
+struct cell_segment *integer_segment;
 #endif
 
 pointer free_cell;       /* pointer to top of free cells */
index aa0cf69..08b53a1 100644 (file)
@@ -725,9 +725,26 @@ get_tag(scheme *sc, pointer v)
 
 \f
 
+/* Low-level allocator.
+ *
+ * Memory is allocated in segments.  Every segment holds a fixed
+ * number of cells.  Segments are linked into a list, sorted in
+ * reverse address order (i.e. those with a higher address first).
+ * This is used in the garbage collector to build the freelist in
+ * address order.
+ */
+
+struct cell_segment
+{
+     struct cell_segment *next;
+     void *alloc;
+     pointer cells;
+     size_t cells_len;
+};
+
 /* Allocate a new cell segment but do not make it available yet.  */
 static int
-_alloc_cellseg(scheme *sc, size_t len, void **alloc, pointer *cells)
+_alloc_cellseg(scheme *sc, size_t len, struct cell_segment **segment)
 {
   int adj = ADJ;
   void *cp;
@@ -735,46 +752,64 @@ _alloc_cellseg(scheme *sc, size_t len, void **alloc, pointer *cells)
   if (adj < sizeof(struct cell))
     adj = sizeof(struct cell);
 
-  cp = sc->malloc(len * sizeof(struct cell) + adj);
+  /* The segment header is conveniently allocated with the cells.  */
+  cp = sc->malloc(sizeof **segment + len * sizeof(struct cell) + adj);
   if (cp == NULL)
     return 1;
 
-  *alloc = cp;
+  *segment = cp;
+  (*segment)->next = NULL;
+  (*segment)->alloc = cp;
+  cp = (void *) ((uintptr_t) cp + sizeof **segment);
 
   /* adjust in TYPE_BITS-bit boundary */
   if (((uintptr_t) cp) % adj != 0)
     cp = (void *) (adj * ((uintptr_t) cp / adj + 1));
 
-  *cells = cp;
+  (*segment)->cells = cp;
+  (*segment)->cells_len = len;
   return 0;
 }
 
+/* Deallocate a cell segment.  Returns the next cell segment.
+ * Convenient for deallocation in a loop.  */
+static struct cell_segment *
+_dealloc_cellseg(scheme *sc, struct cell_segment *segment)
+{
+
+  struct cell_segment *next;
+
+  if (segment == NULL)
+    return NULL;
+
+  next = segment->next;
+  sc->free(segment->alloc);
+  return next;
+}
+
 /* allocate new cell segment */
 static int alloc_cellseg(scheme *sc, int n) {
-     pointer newp;
      pointer last;
      pointer p;
-     long i;
      int k;
 
      for (k = 0; k < n; k++) {
-         if (sc->last_cell_seg >= CELL_NSEGMENT - 1)
-              return k;
-        i = ++sc->last_cell_seg;
-        if (_alloc_cellseg(sc, CELL_SEGSIZE, &sc->alloc_seg[i], &newp)) {
-             sc->last_cell_seg--;
+        struct cell_segment *new, **s;
+        if (_alloc_cellseg(sc, CELL_SEGSIZE, &new)) {
              return k;
         }
-         /* insert new segment in address order */
-         sc->cell_seg[i] = newp;
-         while (i > 0 && sc->cell_seg[i - 1] > sc->cell_seg[i]) {
-             p = sc->cell_seg[i];
-             sc->cell_seg[i] = sc->cell_seg[i - 1];
-             sc->cell_seg[--i] = p;
-         }
-         sc->fcells += CELL_SEGSIZE;
-         last = newp + CELL_SEGSIZE - 1;
-         for (p = newp; p <= last; p++) {
+        /* insert new segment in reverse address order */
+        for (s = &sc->cell_segments;
+             *s && (uintptr_t) (*s)->alloc > (uintptr_t) new->alloc;
+             s = &(*s)->next) {
+            /* walk */
+        }
+        new->next = *s;
+        *s = new;
+
+         sc->fcells += new->cells_len;
+         last = new->cells + new->cells_len - 1;
+          for (p = new->cells; p <= last; p++) {
               typeflag(p) = 0;
               cdr(p) = p + 1;
               car(p) = sc->NIL;
@@ -782,13 +817,13 @@ static int alloc_cellseg(scheme *sc, int n) {
          /* insert new cells in address order on free list */
          if (sc->free_cell == sc->NIL || p < sc->free_cell) {
               cdr(last) = sc->free_cell;
-              sc->free_cell = newp;
+              sc->free_cell = new->cells;
          } else {
                p = sc->free_cell;
-               while (cdr(p) != sc->NIL && newp > cdr(p))
+               while (cdr(p) != sc->NIL && (uintptr_t) new->cells > (uintptr_t) cdr(p))
                     p = cdr(p);
                cdr(last) = cdr(p);
-               cdr(p) = newp;
+               cdr(p) = new->cells;
          }
      }
      return n;
@@ -922,7 +957,7 @@ static pointer _get_cell(scheme *sc, pointer a, pointer b) {
 
   assert (gc_enabled (sc));
   if (sc->free_cell == sc->NIL) {
-    const int min_to_be_recovered = sc->last_cell_seg*8;
+    const int min_to_be_recovered = CELL_SEGSIZE / 4;
     gc(sc,a, b);
     if (sc->fcells < min_to_be_recovered
         || sc->free_cell == sc->NIL) {
@@ -1283,12 +1318,11 @@ static int
 initialize_small_integers(scheme *sc)
 {
   int i;
-  if (_alloc_cellseg(sc, MAX_SMALL_INTEGER, &sc->integer_alloc,
-                    &sc->integer_cells))
+  if (_alloc_cellseg(sc, MAX_SMALL_INTEGER, &sc->integer_segment))
     return 1;
 
   for (i = 0; i < MAX_SMALL_INTEGER; i++) {
-    pointer x = &sc->integer_cells[i];
+    pointer x = &sc->integer_segment->cells[i];
     typeflag(x) = T_NUMBER | T_ATOM | MARK;
     ivalue_unchecked(x) = i;
     set_num_integer(x);
@@ -1302,7 +1336,7 @@ mk_small_integer(scheme *sc, long n)
 {
 #define mk_small_integer_allocates     0
   assert(0 <= n && n < MAX_SMALL_INTEGER);
-  return &sc->integer_cells[n];
+  return &sc->integer_segment->cells[n];
 }
 #else
 
@@ -1666,6 +1700,7 @@ E6:   /* up.  Undo the link switching from steps E4 and E5. */
 /* garbage collection. parameter a, b is marked. */
 static void gc(scheme *sc, pointer a, pointer b) {
   pointer p;
+  struct cell_segment *s;
   int i;
 
   assert (gc_enabled (sc));
@@ -1712,9 +1747,9 @@ static void gc(scheme *sc, pointer a, pointer b) {
      (which are also kept sorted by address) downwards to build the
      free-list in sorted order.
   */
-  for (i = sc->last_cell_seg; i >= 0; i--) {
-    p = sc->cell_seg[i] + CELL_SEGSIZE;
-    while (--p >= sc->cell_seg[i]) {
+  for (s = sc->cell_segments; s; s = s->next) {
+    p = s->cells + s->cells_len;
+    while (--p >= s->cells) {
       if ((typeflag(p) & 1) == 0)
        /* All types have the LSB set.  This is not a typeflag.  */
        continue;
@@ -5592,7 +5627,6 @@ int scheme_init_custom_alloc(scheme *sc, func_alloc malloc, func_dealloc free) {
   sc->gensym_cnt=0;
   sc->malloc=malloc;
   sc->free=free;
-  sc->last_cell_seg = -1;
   sc->sink = &sc->_sink;
   sc->NIL = &sc->_NIL;
   sc->T = &sc->_HASHT;
@@ -5626,6 +5660,7 @@ int scheme_init_custom_alloc(scheme *sc, func_alloc malloc, func_dealloc free) {
   }
   sc->strbuff_size = STRBUFFSIZE;
 
+  sc->cell_segments = NULL;
   if (alloc_cellseg(sc,FIRST_CELLSEGS) != FIRST_CELLSEGS) {
     sc->no_memory=1;
     return 0;
@@ -5726,6 +5761,7 @@ void scheme_set_external_data(scheme *sc, void *p) {
 }
 
 void scheme_deinit(scheme *sc) {
+  struct cell_segment *s;
   int i;
 
   sc->oblist=sc->NIL;
@@ -5758,11 +5794,11 @@ void scheme_deinit(scheme *sc) {
   gc(sc,sc->NIL,sc->NIL);
 
 #if USE_SMALL_INTEGERS
-  sc->free(sc->integer_alloc);
+  _dealloc_cellseg(sc, sc->integer_segment);
 #endif
 
-  for(i=0; i<=sc->last_cell_seg; i++) {
-    sc->free(sc->alloc_seg[i]);
+  for (s = sc->cell_segments; s; s = _dealloc_cellseg(sc, s)) {
+    /* nop */
   }
   sc->free(sc->strbuff);
 }