aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2024-07-08 19:58:22 -0400
committerGravatar Peter McGoron 2024-07-08 19:58:22 -0400
commit4f7847b2c0219995c4fe2d0858b5030e73581a3c (patch)
tree9b84a1fc5698a4f8b97c0580fccf2b26c000d748 /include
parentdelete for hashtables (diff)
Major API reorganization, test infrastructure
The API is now hidden behind functions. The GC struct can have an unstable layout without affecting any compiled binaries (users of Universal Service or collectors hidden behind the API). The collectors can be called directly if desired. The API now allows for different allocation flags. These will be used in future extensions (like weak pointers). For now none are used. Tests are compiled for each collector. I can't think of a good solution that will encompass everything I want to write, but for now this will work on POSIX systems.
Diffstat (limited to 'include')
-rw-r--r--include/cheney_c89.h43
-rw-r--r--include/internal/c89_relo.h52
-rw-r--r--include/uns.h264
3 files changed, 248 insertions, 111 deletions
diff --git a/include/cheney_c89.h b/include/cheney_c89.h
index 32f379a..c6caa66 100644
--- a/include/cheney_c89.h
+++ b/include/cheney_c89.h
@@ -20,13 +20,44 @@
#include "uns.h"
-/* Cheney copying collector in pure portable C89. */
+/* Cheney copying collector in pure portable C89.
+ *
+ * The copying collector does not support weak references or mixed
+ * records. Every collection calls malloc() to obtain a new heap.
+ *
+ * This collector is useful for hosted implementations that want a
+ * resizable heap.
+ */
+
+struct uns_cheney_c89_statistics {
+ size_t usage_before;
+ size_t usage_after;
+ size_t collection_number;
+};
+
+typedef void (*uns_cheney_c89_collect_callback)
+ (Uns_GC, struct uns_cheney_c89_statistics *)
+;
+
+void uns_cheney_c89_set_new_heap_size(Uns_GC gc, size_t siz);
+size_t uns_cheney_c89_get_new_heap_size(Uns_GC gc);
+void uns_cheney_c89_set_collect_callback(
+ Uns_GC gc,
+ uns_cheney_c89_collect_callback cb
+);
+
+void *uns_cheney_c89_get(Uns_ptr p, size_t loc,
+ enum uns_fld_type *typ);
+void uns_cheney_c89_set(Uns_ptr p, size_t loc,
+ enum uns_fld_type typ, void *newp);
-void uns_cheney_c89_deinit(struct uns_gc *gc);
-int uns_cheney_c89_init(struct uns_gc *gc);
-int uns_cheney_c89_collect(struct uns_gc *gc);
-void *uns_cheney_c89_alloc(struct uns_gc *gc, size_t bytes);
-void *uns_cheney_c89_alloc_record(struct uns_gc *gc, size_t records);
+void uns_cheney_c89_deinit(Uns_GC gc);
+int uns_cheney_c89_collect(Uns_GC gc);
+void *uns_cheney_c89_alloc(Uns_GC gc, size_t elems,
+ enum uns_bytes_type typ);
+void *uns_cheney_c89_alloc_rec(Uns_GC gc, size_t elems,
+ enum uns_record_type typ);
+int uns_cheney_c89_init(Uns_GC gc, size_t heap_size);
const extern size_t uns_cheney_c89_ctx_size;
diff --git a/include/internal/c89_relo.h b/include/internal/c89_relo.h
deleted file mode 100644
index 1226ce2..0000000
--- a/include/internal/c89_relo.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef UNS_FORMAT_C89_H
-#define UNS_FORMAT_C89_H
-/* Copyright (C) 2024 Peter McGoron
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
-*/
-
-#include "uns.h"
-
-/* This is a strictly C89+ header format with relocation pointers and
- * lengths.
- *
- * Forwarding pointers need to be stored in the header because in strict
- * C, arithmetic on pointers is undefined for pointers not pointing into
- * the same object.
- *
- * The length of the string is limited to "len" bytes and is stored as
- * a `uns_sword`. If the word is negative, then the region is a record,
- * and if the number is positive then the region is just bytes.
- */
-
-struct uns_c89_relo_hdr {
- void *relo;
- uns_sword len;
-};
-#define uns_c89_relo_get_hdr(p)((struct uns_c89_relo_hdr *)(p) - 1)
-
-size_t uns_c89_relo_get_len(void *p);
-size_t uns_c89_relo_get_record_len(void *p);
-
-#define uns_c89_relo_get_relo(p) (uns_c89_relo_get_hdr(p)->relo)
-#define uns_c89_relo_set_relo(p, v) (uns_c89_relo_get_relo(p) = (v))
-#define uns_c89_relo_is_record(p) (uns_c89_relo_get_hdr(p)->len < 0)
-
-void *uns_c89_relo_init(void *p, size_t len_size, int is_record);
-
-void uns_c89_relo_record_set(Uns_ptr rec, size_t i, void *v);
-void *uns_c89_relo_record_get(Uns_ptr rec, size_t i);
-
-#endif
diff --git a/include/uns.h b/include/uns.h
index d88397c..0ea61cc 100644
--- a/include/uns.h
+++ b/include/uns.h
@@ -18,6 +18,24 @@
*/
#include <stddef.h>
+
+/********************************
+ * Compatability stuff.
+ ********************************/
+#if __STDC_VERSION__ >= 199101L
+# define UNS_INLINE inline
+# include <stdint.h>
+#else
+# define UNS_INLINE
+# define SIZE_MAX ((size_t)0 - 1)
+#endif
+
+#if defined(__GNUC__)
+# define UNS_UNUSED __attribute__((unused))
+#else
+# define UNS_UNUSED
+#endif
+
#if !defined(UNS_WORD) && __STDC_VERSION__ >= 199101L
# include <stdint.h>
# include <limits.h>
@@ -38,75 +56,215 @@
typedef UNS_WORD uns_word;
typedef UNS_SIGNED_WORD uns_sword;
-/* This struct doesn't exist and is used to make a unique pointer for
- * type checking.
- *
- * This pointer must be what was returned from an allocation (i.e. it points
- * to the first byte but before any hidden header data).
+/** Type for a pointer to a region.
+ *
+ * This struct doesn't exist and is used to make a unique pointer for
+ * type checking.
+ *
+ * This pointer must be what was returned from an allocation (i.e. it
+ * points to the first byte but before any hidden header data).
*/
typedef struct uns_ptr *Uns_ptr;
-/** Doubly linked list used to
+/** Flags for bytes regions. */
+enum uns_bytes_type {
+ UNS_NOEXEC = 0
+};
+
+/** Flags for record regions. */
+enum uns_record_type {
+ UNS_POINTERS_ONLY = 0
+};
+
+/** Type of a value in a record. */
+enum uns_fld_type {
+ UNS_POINTER,
+ UNS_INTEGER,
+
+ /** Value is a weak pointer. */
+ UNS_WEAK
+};
+
+/** Container that boxes pointers for the runtime.
+ *
+ * Used for
*
* 1) Store roots of the GC,
* 2) Store GC pointers used in C and possibly not pointed-to anywhere
* else in GC space,
* 3) Preserve the location of a pointer after it moves.
- *
- * A root that is not a part of any GC MUST have ``prev`` and ``next``
- * set to NULL.
*/
struct uns_ctr {
- struct uns_ctr *prev;
+ /** Pointer to a region in the heap, or NULL.
+ *
+ * All roots must point into the same heap, or to NULL.
+ * Multiple roots are allowed to point to the same region.
+ */
void *p;
+
+ /** This field is managed by the library.
+ * This must be set to NULL when initializing the container and
+ * must not be touched while the container is in the root list.
+ */
+ struct uns_ctr *prev;
+
+ /** This field is managed by the library.
+ * This must be set to NULL when initializing the container and
+ * must not be touched while the container is in the root list.
+ */
struct uns_ctr *next;
};
-struct uns_gc {
- void *ctx;
-
- /* The collector only changes the pointer values of "p".
- *
- * * The collector never adds or removes roots.
- * * The collector never rearranges the order of the container structures.
- *
- * The roots list can have repeated values.
- */
- struct uns_ctr *roots;
- size_t next_alloc;
- void (*oom)(struct uns_gc *);
- void (*after_gc)(struct uns_gc *);
-
- size_t before_collection;
- size_t after_collection;
- long collection_number;
-
- void (*deinit)(struct uns_gc *);
-
- /* These functions, and only these functions, may invalidate
- * pointers by either moving them or freeing them.
- */
- int (*collect)(struct uns_gc *);
- void *(*alloc)(struct uns_gc *, size_t);
- void *(*alloc_record)(struct uns_gc *, size_t);
-
- /* These functions MUST NOT cause collections. */
-
- /* This function is optional. It is only provided if the GC can
- * calculate the length of the pointer quickly.
- */
- size_t (*len)(void *);
-
- void (*record_set_ptr)(Uns_ptr, size_t, void *);
- void *(*record_get_ptr)(Uns_ptr, size_t);
-};
+/** Opaque struct that stores the state of a garbage collected heap.
+ *
+ * The only thing that is stable about the layout of this struct is
+ * that the first field is ALWAYS a pointer to the collector context.
+ *
+ * The context pointer will always be the same pointer from init to
+ * deinit.
+ */
+typedef struct uns_gc *Uns_GC;
+
+/** Size of `struct uns_gc` for allocation. */
+extern const size_t uns_gc_size;
+
+/** Get the context pointer for the collector. */
+UNS_UNUSED static UNS_INLINE void *uns_ctx(Uns_GC gc)
+{
+ return *((void **)gc);
+}
+
+/** Set the context pointer for the collector. */
+UNS_UNUSED static UNS_INLINE void uns_set_ctx(Uns_GC gc, void *ctx)
+{
+ *((void **)gc) = ctx;
+}
+
+/** Zero the memory of the generic GC struct. This will leak
+ * memory if a collector was initialized and not deinitialized
+ * beforehand.
+ *
+ * This must be called when the structure is allocated.
+ */
+void uns_gc_zero(Uns_GC gc);
+
+/** Free memory associated with the GC. It is OK to call this
+ * with a GC that has not been initialized.
+ */
+void uns_deinit(Uns_GC gc);
+
+/** Set deinit function. */
+void uns_set_deinit(Uns_GC gc, void(*deinit)(Uns_GC));
-/** Add a root to the GC. The root must point to valid memory, or NULL. */
-void uns_root_add(struct uns_gc *gc, struct uns_ctr *root);
+/** Set OOM handler. */
+void uns_set_oom(Uns_GC gc, void(*oom)(Uns_GC));
+
+/** Call OOM handler. */
+void uns_on_oom(Uns_GC gc);
+
+/** Set garbage collection function. */
+void uns_set_collect(Uns_GC gc, int(*collect)(Uns_GC));
+
+/** Set allocation function. */
+void uns_set_alloc(Uns_GC gc,
+ void*(*alloc)(Uns_GC, size_t, enum uns_bytes_type));
+
+/** Set allocation function. */
+void uns_set_alloc_rec(Uns_GC gc,
+ void*(*alloc_rec)(Uns_GC, size_t, enum uns_record_type));
+
+/** Set record setter function. */
+void uns_set_set(Uns_GC gc, void(*set)(Uns_ptr, size_t, enum uns_fld_type,
+ void*));
+
+/** Set record getter function. */
+void uns_set_get(Uns_GC gc, void*(*get)(Uns_ptr, size_t,
+ enum uns_fld_type *));
+
+/** Force a collection. */
+int uns_collect(Uns_GC gc);
+
+/** Allocate a region of bytes.
+ *
+ * This function will only return NULL if:
+ *
+ * 1. The type of region requested in `typ` is not supported, or
+ * 2. The collector ran out of memory and the out-of-memory handler
+ * did not exit the program.
+ *
+ * Results are undefined if the type is not supported, but all
+ * allocators can be compiled with a mode to abort in those cases.
+ */
+void *uns_alloc(Uns_GC gc, size_t bytes, enum uns_bytes_type typ);
+
+/** Allocate a record.
+ *
+ * This function will only return NULL if:
+ *
+ * 1. The type of region requested in `typ` is not supported, or
+ * 2. The collector ran out of memory and the out-of-memory handler
+ * did not exit the program.
+ *
+ * `len` is the number of elements in the record.
+ */
+void *uns_alloc_rec(Uns_GC gc, size_t len, enum uns_record_type typ);
+
+/** Free a region.
+ *
+ * This is a no-op if freeing is not supported by the collector, or
+ * if `p` is NULL.
+ */
+void uns_free(Uns_GC gc, Uns_ptr p);
+
+/** Get a field from a record.
+ *
+ * ## Parameters
+ * - `notnull gc`: GC data.
+ * - `notnull rec`: Pointer to a record.
+ * - `ind`: Index of the field in the record. This is not the byte
+ * offset.
+ * - `out typ`: What type of field (integer, weak, etc.) was contained
+ * in the field. May be NULL.
+ */
+void *uns_get(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type *typ);
+
+/** Set a field in a record.
+ *
+ * ## Parameters
+ * - `notnull gc`: GC Data.
+ * - `notnull rec`: Pointer to a record.
+ * - `ind`: Index of the field in the record. This is not the byte
+ * offset.
+ * - `typ`: The type of record to store. Behavior is undefined if the
+ * field type is not supported, either by the record or by the heap.
+ * - `val`: Value to store.
+ */
+void uns_set(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type typ,
+ void *val);
+
+/** Add a root to the GC.
+ *
+ * ## Parameters
+ * - `notnull gc`: GC data.
+ * - `notnull in out root`: Pointer to the container containing the root.
+ * Do not modify any other field in the container, except for p, after
+ * this function returns.
+ */
+void uns_root_add(Uns_GC gc, struct uns_ctr *root);
+
+/** Remove a root from the GC.
+ *
+ * ## Parameters
+ * - `notnull gc`: GC data.
+ * - `notnull in out root`: Pointer to the container containing the
+ * root to be removed. If the root is not a part of any heap (the
+ * prev and next pointers are NULL), then this function is a noop.
+ * This function does not modify p.
+ */
+void uns_root_remove(Uns_GC gc, struct uns_ctr *root);
-/** Remove a root from the GC. It is OK to remove a root that is not a
- * part of any GC using this function (the call will have no effect).
+/** Get the head of the roots list.
*/
-void uns_root_remove(struct uns_gc *gc, struct uns_ctr *root);
+struct uns_ctr *uns_roots(Uns_GC gc);
#endif