<commit_before>/* gc-incremental.c
 * The garbage collector.
 * The name is misleading.  GC is non-incremental at this point.
 *
 * Copyright (c) 1996, 1997
 *	Transvirtual Technologies, Inc.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution 
 * of this file. 
 */

#define	STATS

#include "config.h"
#include "debug.h"
#include "config-std.h"
#include "config-mem.h"
#include "gtypes.h"
#include "gc.h"
#include "gc-mem.h"
#include "locks.h"
#include "thread.h"
#include "jthread.h"
#include "errors.h"
#include "md.h"

/*
 * Object implementing the collector interface.
 */
static struct CollectorImpl {
	Collector 	collector;
	/* XXX include static below here for encapsulation */
} gc_obj;

static gcList gclists[5];
static const int mustfree = 4;		/* temporary list */
static const int white = 3;
static const int grey = 2;
static const int black = 1;
static const int finalise = 0;

static int gc_init = 0;
static volatile int gcRunning = 0;
static volatile bool finalRunning = false;
static timespent gc_time;
static timespent sweep_time;
static void (*walkRootSet)(Collector*);

#if defined(STATS)

static void objectStatsChange(gc_unit*, int);
static void objectStatsPrint(void);

#define	OBJECTSTATSADD(M)	objectStatsChange(M, 1)
#define	OBJECTSTATSREMOVE(M)	objectStatsChange(M, -1)
#define	OBJECTSTATSPRINT()	objectStatsPrint()

#else

#define	OBJECTSTATSADD(M)
#define	OBJECTSTATSREMOVE(M)
#define	OBJECTSTATSPRINT()

#endif

/* For statistics gathering, record how many objects and how 
 * much memory was marked.
 */
static inline
void record_marked(int nr_of_objects, uint32 size)
{       
        gcStats.markedobj += nr_of_objects;
        gcStats.markedmem += size;
} 

static iLock gcman;
static iLock finman;
iLock gc_lock;			/* allocator mutex */

static void gcFree(Collector* gcif, void* mem);

/* Standard GC function sets.  We call them "allocation types" now */
static gcFuncs gcFunctions[GC_ALLOC_MAX_INDEX];

/* Number of registered allocation types */
static int nrTypes;

/*
 * register an allocation type under a certain index
 * NB: we could instead return a pointer to the record describing the
 * allocation type.  This would give us more flexibility, but it wouldn't
 * allow us to use compile-time constants.
 */
static void
registerTypeByIndex(int index, walk_func_t walk, final_func_t final,
	destroy_func_t destroy,
	const char *description)
{
	/* once only, please */
	assert (gcFunctions[index].description == 0);
	/* don't exceed bounds */
	assert (index >= 0 && 
		index < sizeof(gcFunctions)/sizeof(gcFunctions[0]));
	gcFunctions[index].walk = walk;
	gcFunctions[index].final = final;
	gcFunctions[index].destroy = destroy;
	gcFunctions[index].description = description;
	if (index >= nrTypes) {
		nrTypes = index + 1;
	}
}

/*
 * Register a fixed allocation type.  The only reason we tell them apart
 * is for statistical purposes.
 */
static void
gcRegisterFixedTypeByIndex(Collector* gcif, 
	int index, const char *description)
{
	registerTypeByIndex(index, 0, GC_OBJECT_FIXED, 0, description);
}

/*
 * Register a allocation type that is subject to gc.  
 */
static void
gcRegisterGcTypeByIndex(Collector* gcif,
	int index, walk_func_t walk, final_func_t final,
	destroy_func_t destroy,
	const char *description)
{
	registerTypeByIndex(index, walk, final, destroy, description);
}

struct _gcStats gcStats;

static void startGC(Collector *gcif);
static void finishGC(Collector *gcif);
static void markObjectDontCheck(gc_unit *unit, gc_block *info, int idx);

/* Return true if gc_unit is pointer to an allocated object */
static inline int
gc_heap_isobject(gc_block *info, gc_unit *unit)
{
	uintp p = (uintp) UTOMEM(unit) - gc_heap_base;
	int idx;

	if (!(p & (MEMALIGN - 1)) && p < gc_heap_range && info->inuse) {
		/* Make sure 'unit' refers to the beginning of an
		 * object.  We do this by making sure it is correctly
		 * aligned within the block.
		 */
		idx = GCMEM2IDX(info, unit);
		if (idx < info->nr && GCBLOCK2MEM(info, idx) == unit
		    && (GC_GET_COLOUR(info, idx) & GC_COLOUR_INUSE) ==
		    GC_COLOUR_INUSE) {
			return 1;
		}
	}
	return 0;
}

/*
 * Mark the memory given by an address if it really is an object.
 */
static void
gcMarkAddress(Collector* gcif, const void* mem)
{
	gc_block* info;
	gc_unit* unit;

	/*
	 * First we check to see if the memory 'mem' is in fact the
	 * beginning of an object.  If not we just return.
	 */

	/* Get block info for this memory - if it exists */
	info = GCMEM2BLOCK(mem);
	unit = UTOUNIT(mem);
	if (gc_heap_isobject(info, unit)) {
		markObjectDontCheck(unit, info, GCMEM2IDX(info, unit));
	}
}

static void
markObjectDontCheck(gc_unit *unit, gc_block *info, int idx)
{
	/* If object's been traced before, don't do it again */
	if (GC_GET_COLOUR(info, idx) != GC_COLOUR_WHITE) {
		return;
	}
DBG(GCWALK,	
	dprintf("  marking @%08p: %s\n", UTOMEM(unit),
			describeObject(UTOMEM(unit)));
    )

	/* If we found a new white object, mark it as grey and
	 * move it into the grey list.
	 */
	GC_SET_COLOUR(info, idx, GC_COLOUR_GREY);
	UREMOVELIST(unit);
	UAPPENDLIST(gclists[grey], unit);
}

/*
 * Mark an object.  Argument is assumed to point to a valid object.
 */
static void
gcMarkObject(Collector* gcif, const void* objp)
{
	if (objp != 0) {
		gc_unit *unit = UTOUNIT(objp);
		gc_block *info = GCMEM2BLOCK(unit);
		int idx = GCMEM2IDX(info, unit);
		markObjectDontCheck(unit, info, idx);
	}
}

static void
gcWalkConservative(Collector* gcif, const void* base, uint32 size)
{
	int8* mem;

DBG(GCWALK,	
	dprintf("scanning %d bytes conservatively from %p-%p\n", 
		size, base, base+size);
    )<commit_msg>added test in DEBUG mode that we don't mark objects that aren't objects.<commit_after>/* gc-incremental.c
 * The garbage collector.
 * The name is misleading.  GC is non-incremental at this point.
 *
 * Copyright (c) 1996, 1997
 *	Transvirtual Technologies, Inc.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution 
 * of this file. 
 */

#define	STATS

#include "config.h"
#include "debug.h"
#include "config-std.h"
#include "config-mem.h"
#include "gtypes.h"
#include "gc.h"
#include "gc-mem.h"
#include "locks.h"
#include "thread.h"
#include "jthread.h"
#include "errors.h"
#include "md.h"

/*
 * Object implementing the collector interface.
 */
static struct CollectorImpl {
	Collector 	collector;
	/* XXX include static below here for encapsulation */
} gc_obj;

static gcList gclists[5];
static const int mustfree = 4;		/* temporary list */
static const int white = 3;
static const int grey = 2;
static const int black = 1;
static const int finalise = 0;

static int gc_init = 0;
static volatile int gcRunning = 0;
static volatile bool finalRunning = false;
static timespent gc_time;
static timespent sweep_time;
static void (*walkRootSet)(Collector*);

#if defined(STATS)

static void objectStatsChange(gc_unit*, int);
static void objectStatsPrint(void);

#define	OBJECTSTATSADD(M)	objectStatsChange(M, 1)
#define	OBJECTSTATSREMOVE(M)	objectStatsChange(M, -1)
#define	OBJECTSTATSPRINT()	objectStatsPrint()

#else

#define	OBJECTSTATSADD(M)
#define	OBJECTSTATSREMOVE(M)
#define	OBJECTSTATSPRINT()

#endif

/* For statistics gathering, record how many objects and how 
 * much memory was marked.
 */
static inline
void record_marked(int nr_of_objects, uint32 size)
{       
        gcStats.markedobj += nr_of_objects;
        gcStats.markedmem += size;
} 

static iLock gcman;
static iLock finman;
iLock gc_lock;			/* allocator mutex */

static void gcFree(Collector* gcif, void* mem);

/* Standard GC function sets.  We call them "allocation types" now */
static gcFuncs gcFunctions[GC_ALLOC_MAX_INDEX];

/* Number of registered allocation types */
static int nrTypes;

/*
 * register an allocation type under a certain index
 * NB: we could instead return a pointer to the record describing the
 * allocation type.  This would give us more flexibility, but it wouldn't
 * allow us to use compile-time constants.
 */
static void
registerTypeByIndex(int index, walk_func_t walk, final_func_t final,
	destroy_func_t destroy,
	const char *description)
{
	/* once only, please */
	assert (gcFunctions[index].description == 0);
	/* don't exceed bounds */
	assert (index >= 0 && 
		index < sizeof(gcFunctions)/sizeof(gcFunctions[0]));
	gcFunctions[index].walk = walk;
	gcFunctions[index].final = final;
	gcFunctions[index].destroy = destroy;
	gcFunctions[index].description = description;
	if (index >= nrTypes) {
		nrTypes = index + 1;
	}
}

/*
 * Register a fixed allocation type.  The only reason we tell them apart
 * is for statistical purposes.
 */
static void
gcRegisterFixedTypeByIndex(Collector* gcif, 
	int index, const char *description)
{
	registerTypeByIndex(index, 0, GC_OBJECT_FIXED, 0, description);
}

/*
 * Register a allocation type that is subject to gc.  
 */
static void
gcRegisterGcTypeByIndex(Collector* gcif,
	int index, walk_func_t walk, final_func_t final,
	destroy_func_t destroy,
	const char *description)
{
	registerTypeByIndex(index, walk, final, destroy, description);
}

struct _gcStats gcStats;

static void startGC(Collector *gcif);
static void finishGC(Collector *gcif);
static void markObjectDontCheck(gc_unit *unit, gc_block *info, int idx);

/* Return true if gc_unit is pointer to an allocated object */
static inline int
gc_heap_isobject(gc_block *info, gc_unit *unit)
{
	uintp p = (uintp) UTOMEM(unit) - gc_heap_base;
	int idx;

	if (!(p & (MEMALIGN - 1)) && p < gc_heap_range && info->inuse) {
		/* Make sure 'unit' refers to the beginning of an
		 * object.  We do this by making sure it is correctly
		 * aligned within the block.
		 */
		idx = GCMEM2IDX(info, unit);
		if (idx < info->nr && GCBLOCK2MEM(info, idx) == unit
		    && (GC_GET_COLOUR(info, idx) & GC_COLOUR_INUSE) ==
		    GC_COLOUR_INUSE) {
			return 1;
		}
	}
	return 0;
}

/*
 * Mark the memory given by an address if it really is an object.
 */
static void
gcMarkAddress(Collector* gcif, const void* mem)
{
	gc_block* info;
	gc_unit* unit;

	/*
	 * First we check to see if the memory 'mem' is in fact the
	 * beginning of an object.  If not we just return.
	 */

	/* Get block info for this memory - if it exists */
	info = GCMEM2BLOCK(mem);
	unit = UTOUNIT(mem);
	if (gc_heap_isobject(info, unit)) {
		markObjectDontCheck(unit, info, GCMEM2IDX(info, unit));
	}
}

static void
markObjectDontCheck(gc_unit *unit, gc_block *info, int idx)
{
	/* If object's been traced before, don't do it again */
	if (GC_GET_COLOUR(info, idx) != GC_COLOUR_WHITE) {
		return;
	}
DBG(GCWALK,	
	dprintf("  marking @%08p: %s\n", UTOMEM(unit),
			describeObject(UTOMEM(unit)));
    )

	/* If we found a new white object, mark it as grey and
	 * move it into the grey list.
	 */
	GC_SET_COLOUR(info, idx, GC_COLOUR_GREY);
	UREMOVELIST(unit);
	UAPPENDLIST(gclists[grey], unit);
}

/*
 * Mark an object.  Argument is assumed to point to a valid object.
 */
static void
gcMarkObject(Collector* gcif, const void* objp)
{
	if (objp != 0) {
		gc_unit *unit = UTOUNIT(objp);
		gc_block *info = GCMEM2BLOCK(unit);
#if defined(DEBUG)
		assert(gc_heap_isobject(info, unit));
#endif
		markObjectDontCheck(unit, info, GCMEM2IDX(info, unit));
	}
}

static void
gcWalkConservative(Collector* gcif, const void* base, uint32 size)
{
	int8* mem;

DBG(GCWALK,	
	dprintf("scanning %d bytes conservatively from %p-%p\n", 
		size, base, base+size);
    )
{'fraction_non_alphanumeric': 0.11131514515564883, 'fraction_numerical': 0.007170339279468346, 'mean_word_length': 4.2199908717480605, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2995611', 'n_tokens_mistral': 4105, 'n_tokens_neox': 3881, 'n_words': 1266}