Initial revision
This commit is contained in:
		
							
								
								
									
										317
									
								
								gc/stubborn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								gc/stubborn.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,317 @@ | ||||
| /*  | ||||
|  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers | ||||
|  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved. | ||||
|  * | ||||
|  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED | ||||
|  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. | ||||
|  * | ||||
|  * Permission is hereby granted to use or copy this program | ||||
|  * for any purpose,  provided the above notices are retained on all copies. | ||||
|  * Permission to modify the code and to distribute modified code is granted, | ||||
|  * provided the above notices are retained, and a notice that the code was | ||||
|  * modified is included with the above copyright notice. | ||||
|  */ | ||||
| /* Boehm, July 31, 1995 5:02 pm PDT */ | ||||
|  | ||||
|  | ||||
| #include "gc_priv.h" | ||||
|  | ||||
| # ifdef STUBBORN_ALLOC | ||||
| /* Stubborn object (hard to change, nearly immutable) allocation. */ | ||||
|  | ||||
| extern ptr_t GC_clear_stack();	/* in misc.c, behaves like identity */ | ||||
|  | ||||
| #define GENERAL_MALLOC(lb,k) \ | ||||
|     (GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k)) | ||||
|  | ||||
| /* Data structure representing immutable objects that 	*/ | ||||
| /* are still being initialized.				*/ | ||||
| /* This is a bit baroque in order to avoid acquiring	*/ | ||||
| /* the lock twice for a typical allocation.		*/ | ||||
|  | ||||
| GC_PTR * GC_changing_list_start; | ||||
|  | ||||
| # ifdef THREADS | ||||
|   VOLATILE GC_PTR * VOLATILE GC_changing_list_current; | ||||
| # else | ||||
|   GC_PTR * GC_changing_list_current; | ||||
| # endif | ||||
| 	/* Points at last added element.  Also (ab)used for		*/ | ||||
| 	/* synchronization.  Updates and reads are assumed atomic.	*/ | ||||
|  | ||||
| GC_PTR * GC_changing_list_limit; | ||||
| 	/* Points at the last word of the buffer, which is always 0	*/ | ||||
| 	/* All entries in (GC_changing_list_current,			*/ | ||||
| 	/* GC_changing_list_limit] are 0				*/ | ||||
|  | ||||
|  | ||||
| void GC_stubborn_init() | ||||
| { | ||||
| #   define INIT_SIZE 10 | ||||
|  | ||||
|     GC_changing_list_start = (GC_PTR *) | ||||
|     			GC_generic_malloc_inner( | ||||
|     				(word)(INIT_SIZE * sizeof(GC_PTR)), | ||||
|     				PTRFREE); | ||||
|     BZERO(GC_changing_list_start, | ||||
|     	  INIT_SIZE * sizeof(GC_PTR)); | ||||
|     if (GC_changing_list_start == 0) { | ||||
|         GC_err_printf0("Insufficient space to start up\n"); | ||||
|         ABORT("GC_stubborn_init: put of space"); | ||||
|     } | ||||
|     GC_changing_list_current = GC_changing_list_start; | ||||
|     GC_changing_list_limit = GC_changing_list_start + INIT_SIZE - 1; | ||||
|     * GC_changing_list_limit = 0; | ||||
| } | ||||
|  | ||||
| /* Compact and possibly grow GC_uninit_list.  The old copy is		*/ | ||||
| /* left alone.	Lock must be held.					*/ | ||||
| /* When called GC_changing_list_current == GC_changing_list_limit	*/ | ||||
| /* which is one past the current element.				*/ | ||||
| /* When we finish GC_changing_list_current again points one past last	*/ | ||||
| /* element.								*/ | ||||
| /* Invariant while this is running: GC_changing_list_current    	*/ | ||||
| /* points at a word containing 0.					*/ | ||||
| /* Returns FALSE on failure.						*/ | ||||
| GC_bool GC_compact_changing_list() | ||||
| { | ||||
|     register GC_PTR *p, *q; | ||||
|     register word count = 0; | ||||
|     word old_size = (char **)GC_changing_list_limit | ||||
|     		    - (char **)GC_changing_list_start+1; | ||||
|     		    /* The casts are needed as a workaround for an Amiga bug */ | ||||
|     register word new_size = old_size; | ||||
|     GC_PTR * new_list; | ||||
|      | ||||
|     for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { | ||||
|         if (*p != 0) count++; | ||||
|     } | ||||
|     if (2 * count > old_size) new_size = 2 * count; | ||||
|     new_list = (GC_PTR *) | ||||
|     		GC_generic_malloc_inner( | ||||
|     				new_size * sizeof(GC_PTR), PTRFREE); | ||||
|     		/* PTRFREE is a lie.  But we don't want the collector to  */ | ||||
|     		/* consider these.  We do want the list itself to be  	  */ | ||||
|     		/* collectable.						  */ | ||||
|     if (new_list == 0) return(FALSE); | ||||
|     BZERO(new_list, new_size * sizeof(GC_PTR)); | ||||
|     q = new_list; | ||||
|     for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { | ||||
|         if (*p != 0) *q++ = *p; | ||||
|     } | ||||
|     GC_changing_list_start = new_list; | ||||
|     GC_changing_list_limit = new_list + new_size - 1; | ||||
|     GC_changing_list_current = q; | ||||
|     return(TRUE); | ||||
| } | ||||
|  | ||||
| /* Add p to changing list.  Clear p on failure.	*/ | ||||
| # define ADD_CHANGING(p) \ | ||||
| 	{	\ | ||||
| 	    register struct hblk * h = HBLKPTR(p);	\ | ||||
| 	    register word index = PHT_HASH(h);	\ | ||||
| 	    \ | ||||
| 	    set_pht_entry_from_index(GC_changed_pages, index);	\ | ||||
| 	}	\ | ||||
| 	if (*GC_changing_list_current != 0 \ | ||||
| 	    && ++GC_changing_list_current == GC_changing_list_limit) { \ | ||||
| 	    if (!GC_compact_changing_list()) (p) = 0; \ | ||||
| 	} \ | ||||
| 	*GC_changing_list_current = p; | ||||
|  | ||||
| void GC_change_stubborn(p) | ||||
| GC_PTR p; | ||||
| { | ||||
|     DCL_LOCK_STATE; | ||||
|      | ||||
|     DISABLE_SIGNALS(); | ||||
|     LOCK(); | ||||
|     ADD_CHANGING(p); | ||||
|     UNLOCK(); | ||||
|     ENABLE_SIGNALS(); | ||||
| } | ||||
|  | ||||
| void GC_end_stubborn_change(p) | ||||
| GC_PTR p; | ||||
| { | ||||
| #   ifdef THREADS | ||||
|       register VOLATILE GC_PTR * my_current = GC_changing_list_current; | ||||
| #   else | ||||
|       register GC_PTR * my_current = GC_changing_list_current; | ||||
| #   endif | ||||
|     register GC_bool tried_quick; | ||||
|     DCL_LOCK_STATE; | ||||
|      | ||||
|     if (*my_current == p) { | ||||
|         /* Hopefully the normal case.					*/ | ||||
|         /* Compaction could not have been running when we started.	*/ | ||||
|         *my_current = 0; | ||||
| #	ifdef THREADS | ||||
|           if (my_current == GC_changing_list_current) { | ||||
|             /* Compaction can't have run in the interim. 	*/ | ||||
|             /* We got away with the quick and dirty approach.   */ | ||||
|             return; | ||||
|           } | ||||
|           tried_quick = TRUE; | ||||
| #	else | ||||
| 	  return; | ||||
| #	endif | ||||
|     } else { | ||||
|         tried_quick = FALSE; | ||||
|     } | ||||
|     DISABLE_SIGNALS(); | ||||
|     LOCK(); | ||||
|     my_current = GC_changing_list_current; | ||||
|     for (; my_current >= GC_changing_list_start; my_current--) { | ||||
|         if (*my_current == p) { | ||||
|             *my_current = 0; | ||||
|             UNLOCK(); | ||||
|             ENABLE_SIGNALS(); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     if (!tried_quick) { | ||||
|         GC_err_printf1("Bad arg to GC_end_stubborn_change: 0x%lx\n", | ||||
|         	       (unsigned long)p); | ||||
|         ABORT("Bad arg to GC_end_stubborn_change"); | ||||
|     } | ||||
|     UNLOCK(); | ||||
|     ENABLE_SIGNALS(); | ||||
| } | ||||
|  | ||||
| /* Allocate lb bytes of composite (pointerful) data	*/ | ||||
| /* No pointer fields may be changed after a call to	*/ | ||||
| /* GC_end_stubborn_change(p) where p is the value	*/ | ||||
| /* returned by GC_malloc_stubborn.			*/ | ||||
| # ifdef __STDC__ | ||||
|     GC_PTR GC_malloc_stubborn(size_t lb) | ||||
| # else | ||||
|     GC_PTR GC_malloc_stubborn(lb) | ||||
|     size_t lb; | ||||
| # endif | ||||
| { | ||||
| register ptr_t op; | ||||
| register ptr_t *opp; | ||||
| register word lw; | ||||
| ptr_t result; | ||||
| DCL_LOCK_STATE; | ||||
|  | ||||
|     if( SMALL_OBJ(lb) ) { | ||||
| #       ifdef MERGE_SIZES | ||||
| 	  lw = GC_size_map[lb]; | ||||
| #	else | ||||
| 	  lw = ALIGNED_WORDS(lb); | ||||
| #       endif | ||||
| 	opp = &(GC_sobjfreelist[lw]); | ||||
| 	FASTLOCK(); | ||||
|         if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { | ||||
|             FASTUNLOCK(); | ||||
|             result = GC_generic_malloc((word)lb, STUBBORN); | ||||
|             goto record; | ||||
|         } | ||||
|         *opp = obj_link(op); | ||||
|         obj_link(op) = 0; | ||||
|         GC_words_allocd += lw; | ||||
|         result = (GC_PTR) op; | ||||
|         ADD_CHANGING(result); | ||||
|         FASTUNLOCK(); | ||||
|         return((GC_PTR)result); | ||||
|    } else { | ||||
|        result = (GC_PTR) | ||||
|           	GC_generic_malloc((word)lb, STUBBORN); | ||||
|    } | ||||
| record: | ||||
|    DISABLE_SIGNALS(); | ||||
|    LOCK(); | ||||
|    ADD_CHANGING(result); | ||||
|    UNLOCK(); | ||||
|    ENABLE_SIGNALS(); | ||||
|    return((GC_PTR)GC_clear_stack(result)); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Functions analogous to GC_read_dirty and GC_page_was_dirty.	*/ | ||||
| /* Report pages on which stubborn objects were changed.		*/ | ||||
| void GC_read_changed() | ||||
| { | ||||
|     register GC_PTR * p = GC_changing_list_start; | ||||
|     register GC_PTR q; | ||||
|     register struct hblk * h; | ||||
|     register word index; | ||||
|      | ||||
|     if (p == 0) /* initializing */ return; | ||||
|     BCOPY(GC_changed_pages, GC_prev_changed_pages, | ||||
|           (sizeof GC_changed_pages)); | ||||
|     BZERO(GC_changed_pages, (sizeof GC_changed_pages)); | ||||
|     for (; p <= GC_changing_list_current; p++) { | ||||
|         if ((q = *p) != 0) { | ||||
|             h = HBLKPTR(q); | ||||
|             index = PHT_HASH(h); | ||||
|             set_pht_entry_from_index(GC_changed_pages, index); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| GC_bool GC_page_was_changed(h) | ||||
| struct hblk * h; | ||||
| { | ||||
|     register word index = PHT_HASH(h); | ||||
|      | ||||
|     return(get_pht_entry_from_index(GC_prev_changed_pages, index)); | ||||
| } | ||||
|  | ||||
| /* Remove unreachable entries from changed list. Should only be	*/ | ||||
| /* called with mark bits consistent and lock held.		*/ | ||||
| void GC_clean_changing_list() | ||||
| { | ||||
|     register GC_PTR * p = GC_changing_list_start; | ||||
|     register GC_PTR q; | ||||
|     register ptr_t r; | ||||
|     register unsigned long count = 0; | ||||
|     register unsigned long dropped_count = 0; | ||||
|      | ||||
|     if (p == 0) /* initializing */ return; | ||||
|     for (; p <= GC_changing_list_current; p++) { | ||||
|         if ((q = *p) != 0) { | ||||
|             count++; | ||||
|             r = (ptr_t)GC_base(q); | ||||
|             if (r == 0 || !GC_is_marked(r)) { | ||||
|                 *p = 0; | ||||
|                 dropped_count++; | ||||
| 	    } | ||||
|         } | ||||
|     } | ||||
| #   ifdef PRINTSTATS | ||||
|       if (count > 0) { | ||||
|         GC_printf2("%lu entries in changing list: reclaimed %lu\n", | ||||
|                   (unsigned long)count, (unsigned long)dropped_count); | ||||
|       } | ||||
| #   endif | ||||
| } | ||||
|  | ||||
| #else /* !STUBBORN_ALLOC */ | ||||
|  | ||||
| # ifdef __STDC__ | ||||
|     GC_PTR GC_malloc_stubborn(size_t lb) | ||||
| # else | ||||
|     GC_PTR GC_malloc_stubborn(lb) | ||||
|     size_t lb; | ||||
| # endif | ||||
| { | ||||
|     return(GC_malloc(lb)); | ||||
| } | ||||
|  | ||||
| /*ARGSUSED*/ | ||||
| void GC_end_stubborn_change(p) | ||||
| GC_PTR p; | ||||
| { | ||||
| } | ||||
|  | ||||
| /*ARGSUSED*/ | ||||
| void GC_change_stubborn(p) | ||||
| GC_PTR p; | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user