[Python-checkins] python/dist/src/Modules gcmodule.c,2.40,2.41

nascheme@sourceforge.net nascheme@sourceforge.net
Fri, 03 May 2002 22:35:22 -0700


Update of /cvsroot/python/python/dist/src/Modules
In directory usw-pr-cvs1:/tmp/cvs-serv5412/Modules

Modified Files:
	gcmodule.c 
Log Message:
Move all data for a single generation into a structure.  The set of
generations is now an array.  This cleans up some code and makes it easy
to change the number of generations.  Also, implemented a
gc_list_is_empty() function.  This makes the logic a little clearer in
places.  The performance impact of these changes should be negligible.

One functional change is that allocation/collection counters are always
zeroed at the start of a collection.  This should fix SF bug #551915.
This change is too big for back-porting but the minimal patch on SF
looks good for a bugfix release.


Index: gcmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/gcmodule.c,v
retrieving revision 2.40
retrieving revision 2.41
diff -C2 -d -r2.40 -r2.41
*** gcmodule.c	28 Apr 2002 01:57:25 -0000	2.40
--- gcmodule.c	4 May 2002 05:35:20 -0000	2.41
***************
*** 32,49 ****
  /*** Global GC state ***/
  
  /* linked lists of container objects */
! PyGC_Head _PyGC_generation0 = {{&_PyGC_generation0, &_PyGC_generation0, 0}};
! static PyGC_Head generation1 = {{&generation1, &generation1, 0}};
! static PyGC_Head generation2 = {{&generation2, &generation2, 0}};
! static int generation = 0; /* current generation being collected */
  
! /* collection frequencies, XXX tune these */
! static int enabled = 1; /* automatic collection enabled? */
! static int threshold0 = 700; /* net new containers before collection */
! static int threshold1 = 10;  /* generation0 collections before collecting 1 */
! static int threshold2 = 10;  /* generation1 collections before collecting 2 */
  
! /* net new objects allocated since last collection */
! static int allocated;
  
  /* true if we are currently running the collector */
--- 32,56 ----
  /*** Global GC state ***/
  
+ struct gc_generation {
+ 	PyGC_Head head;
+ 	int threshold; /* collection threshold */
+ 	int count; /* count of allocations or collections of younger
+ 		      generations */
+ };
+ 
+ #define NUM_GENERATIONS 3
+ #define GEN_HEAD(n) (&generations[n].head)
+ 
  /* linked lists of container objects */
! static struct gc_generation generations[NUM_GENERATIONS] = {
! 	/* PyGC_Head,				threshold,	count */
! 	{{{GEN_HEAD(0), GEN_HEAD(0), 0}},	700,		0},
! 	{{{GEN_HEAD(1), GEN_HEAD(1), 0}},	10,		0},
! 	{{{GEN_HEAD(2), GEN_HEAD(2), 0}},	10,		0},
! };
  
! PyGC_Head *_PyGC_generation0 = GEN_HEAD(0);
  
! static int enabled = 1; /* automatic collection enabled? */
  
  /* true if we are currently running the collector */
***************
*** 82,85 ****
--- 89,98 ----
  }
  
+ static int
+ gc_list_is_empty(PyGC_Head *list)
+ {
+ 	return (list->gc.gc_next == list);
+ }
+ 
  static void
  gc_list_append(PyGC_Head *node, PyGC_Head *list)
***************
*** 102,107 ****
  gc_list_move(PyGC_Head *from, PyGC_Head *to)
  {
! 	if (from->gc.gc_next == from) {
! 		/* empty from list */
  		gc_list_init(to);
  	}
--- 115,119 ----
  gc_list_move(PyGC_Head *from, PyGC_Head *to)
  {
! 	if (gc_list_is_empty(from)) {
  		gc_list_init(to);
  	}
***************
*** 120,124 ****
  {
  	PyGC_Head *tail;
! 	if (from->gc.gc_next != from) {
  		tail = to->gc.gc_prev;
  		tail->gc.gc_next = from->gc.gc_next;
--- 132,136 ----
  {
  	PyGC_Head *tail;
! 	if (!gc_list_is_empty(from)) {
  		tail = to->gc.gc_prev;
  		tail->gc.gc_next = from->gc.gc_next;
***************
*** 331,335 ****
  	inquiry clear;
  
! 	while (unreachable->gc.gc_next != unreachable) {
  		PyGC_Head *gc = unreachable->gc.gc_next;
  		PyObject *op = FROM_GC(gc);
--- 343,347 ----
  	inquiry clear;
  
! 	while (!gc_list_is_empty(unreachable)) {
  		PyGC_Head *gc = unreachable->gc.gc_next;
  		PyObject *op = FROM_GC(gc);
***************
*** 355,362 ****
   * collection process works. */
  static long
! collect(PyGC_Head *young, PyGC_Head *old)
  {
  	long n = 0;
  	long m = 0;
  	PyGC_Head reachable;
  	PyGC_Head unreachable;
--- 367,377 ----
   * collection process works. */
  static long
! collect(int generation)
  {
+ 	int i;
  	long n = 0;
  	long m = 0;
+ 	PyGC_Head *young; /* the generation we are examining */
+ 	PyGC_Head *old; /* next older generation */
  	PyGC_Head reachable;
  	PyGC_Head unreachable;
***************
*** 365,375 ****
  
  	if (debug & DEBUG_STATS) {
! 		PySys_WriteStderr(
! 			"gc: collecting generation %d...\n"
! 			"gc: objects in each generation: %ld %ld %ld\n",
! 			generation,
! 			gc_list_size(&_PyGC_generation0),
! 			gc_list_size(&generation1),
! 			gc_list_size(&generation2));
  	}
  
--- 380,409 ----
  
  	if (debug & DEBUG_STATS) {
! 		PySys_WriteStderr("gc: collecting generation %d...\n",
! 				  generation);
! 		PySys_WriteStderr("gc: objects in each generation:");
! 		for (i = 0; i < NUM_GENERATIONS; i++) {
! 			PySys_WriteStderr(" %ld", gc_list_size(GEN_HEAD(i)));
! 		}
! 		PySys_WriteStderr("\n");
! 	}
! 
! 	/* update collection and allocation counters */
! 	if (generation+1 < NUM_GENERATIONS)
! 		generations[generation+1].count += 1;
! 	for (i = 0; i <= generation; i++)
! 		generations[generation].count = 0;
! 
! 	/* merge younger generations with one we are currently collecting */
! 	for (i = 0; i < generation; i++) {
! 		gc_list_merge(GEN_HEAD(i), GEN_HEAD(generation));
! 	}
! 
! 	/* handy references */
! 	young = GEN_HEAD(generation);
! 	if (generation < NUM_GENERATIONS-1) {
! 		old = GEN_HEAD(generation+1);
! 	} else {
! 		old = GEN_HEAD(NUM_GENERATIONS-1);
  	}
  
***************
*** 450,454 ****
  		Py_FatalError("unexpected exception during garbage collection");
  	}
- 	allocated = 0;
  	return n+m;
  }
--- 484,487 ----
***************
*** 457,488 ****
  collect_generations(void)
  {
! 	static long collections0 = 0;
! 	static long collections1 = 0;
  	long n = 0;
  
! 
! 	if (collections1 > threshold2) {
! 		generation = 2;
! 		gc_list_merge(&_PyGC_generation0, &generation2);
! 		gc_list_merge(&generation1, &generation2);
! 		if (generation2.gc.gc_next != &generation2) {
! 			n = collect(&generation2, &generation2);
! 		}
! 		collections1 = 0;
! 	}
! 	else if (collections0 > threshold1) {
! 		generation = 1;
! 		collections1++;
! 		gc_list_merge(&_PyGC_generation0, &generation1);
! 		if (generation1.gc.gc_next != &generation1) {
! 			n = collect(&generation1, &generation2);
! 		}
! 		collections0 = 0;
! 	}
! 	else {
! 		generation = 0;
! 		collections0++;
! 		if (_PyGC_generation0.gc.gc_next != &_PyGC_generation0) {
! 			n = collect(&_PyGC_generation0, &generation1);
  		}
  	}
--- 490,503 ----
  collect_generations(void)
  {
! 	int i;
  	long n = 0;
  
! 	/* Find the oldest generation (higest numbered) where the count
! 	 * exceeds the threshold.  Objects in the that generation and
! 	 * generations younger than it will be collected. */
! 	for (i = NUM_GENERATIONS-1; i >= 0; i--) {
! 		if (generations[i].count > generations[i].threshold) {
! 			n = collect(i);
! 			break;
  		}
  	}
***************
*** 563,570 ****
  	else {
  		collecting = 1;
! 		generation = 2;
! 		gc_list_merge(&_PyGC_generation0, &generation2);
! 		gc_list_merge(&generation1, &generation2);
! 		n = collect(&generation2, &generation2);
  		collecting = 0;
  	}
--- 578,582 ----
  	else {
  		collecting = 1;
! 		n = collect(NUM_GENERATIONS - 1);
  		collecting = 0;
  	}
***************
*** 625,631 ****
  gc_set_thresh(PyObject *self, PyObject *args)
  {
! 	if (!PyArg_ParseTuple(args, "i|ii:set_threshold", &threshold0, 
! 				&threshold1, &threshold2))
  		return NULL;
  
  	Py_INCREF(Py_None);
--- 637,650 ----
  gc_set_thresh(PyObject *self, PyObject *args)
  {
! 	int i;
! 	if (!PyArg_ParseTuple(args, "i|ii:set_threshold",
! 			      &generations[0].threshold,
! 			      &generations[1].threshold,
! 			      &generations[2].threshold))
  		return NULL;
+ 	for (i = 2; i < NUM_GENERATIONS; i++) {
+  		/* generations higher than 2 get the same threshold */
+ 		generations[i].threshold = generations[2].threshold;
+ 	}
  
  	Py_INCREF(Py_None);
***************
*** 645,649 ****
  		return NULL;
  
! 	return Py_BuildValue("(iii)", threshold0, threshold1, threshold2);
  }
  
--- 664,671 ----
  		return NULL;
  
! 	return Py_BuildValue("(iii)",
! 			     generations[0].threshold,
! 			     generations[1].threshold,
! 			     generations[2].threshold);
  }
  
***************
*** 684,693 ****
  gc_get_referrers(PyObject *self, PyObject *args)
  {
  	PyObject *result = PyList_New(0);
! 	if (!(gc_referrers_for(args, &_PyGC_generation0, result) &&
! 	      gc_referrers_for(args, &generation1, result) &&
! 	      gc_referrers_for(args, &generation2, result))) {
! 		Py_DECREF(result);
! 		return NULL;
  	}
  	return result;
--- 706,716 ----
  gc_get_referrers(PyObject *self, PyObject *args)
  {
+ 	int i;
  	PyObject *result = PyList_New(0);
! 	for (i = 0; i < NUM_GENERATIONS; i++) {
! 		if (!(gc_referrers_for(args, GEN_HEAD(i), result))) {
! 			Py_DECREF(result);
! 			return NULL;
! 		}
  	}
  	return result;
***************
*** 720,723 ****
--- 743,747 ----
  gc_get_objects(PyObject *self, PyObject *args)
  {
+ 	int i;
  	PyObject* result;
  
***************
*** 728,736 ****
  		return NULL;
  	}
! 	if (append_objects(result, &_PyGC_generation0) ||
! 	    append_objects(result, &generation1) ||
! 	    append_objects(result, &generation2)) {
! 		Py_DECREF(result);
! 		return NULL;
  	}
  	return result;
--- 752,760 ----
  		return NULL;
  	}
! 	for (i = 0; i < NUM_GENERATIONS; i++) {
! 		if (append_objects(result, GEN_HEAD(i))) {
! 			Py_DECREF(result);
! 			return NULL;
! 		}
  	}
  	return result;
***************
*** 855,866 ****
  		return (PyObject *)PyErr_NoMemory();
  	g->gc.gc_next = NULL;
! 	allocated++;
!  	if (allocated > threshold0 &&
   	    enabled &&
!  	    threshold0 &&
   	    !collecting &&
   	    !PyErr_Occurred()) {
  		collecting = 1;
!  		collect_generations();
  		collecting = 0;
  	}
--- 879,890 ----
  		return (PyObject *)PyErr_NoMemory();
  	g->gc.gc_next = NULL;
! 	generations[0].count++; /* number of allocated GC objects */
!  	if (generations[0].count > generations[0].threshold &&
   	    enabled &&
!  	    generations[0].threshold &&
   	    !collecting &&
   	    !PyErr_Occurred()) {
  		collecting = 1;
! 		collect_generations();
  		collecting = 0;
  	}
***************
*** 920,925 ****
  	if (g->gc.gc_next != NULL)
  		gc_list_remove(g);
! 	if (allocated > 0) {
! 		allocated--;
  	}
  	PyObject_FREE(g);
--- 944,949 ----
  	if (g->gc.gc_next != NULL)
  		gc_list_remove(g);
! 	if (generations[0].count > 0) {
! 		generations[0].count--;
  	}
  	PyObject_FREE(g);