diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 163 |
1 files changed, 104 insertions, 59 deletions
| @@ -3440,6 +3440,7 @@ static unsigned sxhash_string P_ ((unsigned char *, int)); | |||
| 3440 | static unsigned sxhash_list P_ ((Lisp_Object, int)); | 3440 | static unsigned sxhash_list P_ ((Lisp_Object, int)); |
| 3441 | static unsigned sxhash_vector P_ ((Lisp_Object, int)); | 3441 | static unsigned sxhash_vector P_ ((Lisp_Object, int)); |
| 3442 | static unsigned sxhash_bool_vector P_ ((Lisp_Object)); | 3442 | static unsigned sxhash_bool_vector P_ ((Lisp_Object)); |
| 3443 | static int sweep_weak_table P_ ((struct Lisp_Hash_Table *, int)); | ||
| 3443 | 3444 | ||
| 3444 | 3445 | ||
| 3445 | 3446 | ||
| @@ -4022,6 +4023,86 @@ hash_clear (h) | |||
| 4022 | Weak Hash Tables | 4023 | Weak Hash Tables |
| 4023 | ************************************************************************/ | 4024 | ************************************************************************/ |
| 4024 | 4025 | ||
| 4026 | /* Sweep weak hash table H. REMOVE_ENTRIES_P non-zero means remove | ||
| 4027 | entries from the table that don't survive the current GC. | ||
| 4028 | REMOVE_ENTRIES_P zero means mark entries that are in use. Value is | ||
| 4029 | non-zero if anything was marked. */ | ||
| 4030 | |||
| 4031 | static int | ||
| 4032 | sweep_weak_table (h, remove_entries_p) | ||
| 4033 | struct Lisp_Hash_Table *h; | ||
| 4034 | int remove_entries_p; | ||
| 4035 | { | ||
| 4036 | int bucket, n, marked; | ||
| 4037 | |||
| 4038 | n = XVECTOR (h->index)->size & ~ARRAY_MARK_FLAG; | ||
| 4039 | marked = 0; | ||
| 4040 | |||
| 4041 | for (bucket = 0; bucket < n; ++bucket) | ||
| 4042 | { | ||
| 4043 | Lisp_Object idx, prev; | ||
| 4044 | |||
| 4045 | /* Follow collision chain, removing entries that | ||
| 4046 | don't survive this garbage collection. */ | ||
| 4047 | idx = HASH_INDEX (h, bucket); | ||
| 4048 | prev = Qnil; | ||
| 4049 | while (!GC_NILP (idx)) | ||
| 4050 | { | ||
| 4051 | int remove_p; | ||
| 4052 | int i = XFASTINT (idx); | ||
| 4053 | Lisp_Object next; | ||
| 4054 | |||
| 4055 | if (EQ (h->weak, Qkey)) | ||
| 4056 | remove_p = !survives_gc_p (HASH_KEY (h, i)); | ||
| 4057 | else if (EQ (h->weak, Qvalue)) | ||
| 4058 | remove_p = !survives_gc_p (HASH_VALUE (h, i)); | ||
| 4059 | else if (EQ (h->weak, Qt)) | ||
| 4060 | remove_p = (!survives_gc_p (HASH_KEY (h, i)) | ||
| 4061 | || !survives_gc_p (HASH_VALUE (h, i))); | ||
| 4062 | else | ||
| 4063 | abort (); | ||
| 4064 | |||
| 4065 | next = HASH_NEXT (h, i); | ||
| 4066 | |||
| 4067 | if (remove_entries_p) | ||
| 4068 | { | ||
| 4069 | if (remove_p) | ||
| 4070 | { | ||
| 4071 | /* Take out of collision chain. */ | ||
| 4072 | if (GC_NILP (prev)) | ||
| 4073 | HASH_INDEX (h, i) = next; | ||
| 4074 | else | ||
| 4075 | HASH_NEXT (h, XFASTINT (prev)) = next; | ||
| 4076 | |||
| 4077 | /* Add to free list. */ | ||
| 4078 | HASH_NEXT (h, i) = h->next_free; | ||
| 4079 | h->next_free = idx; | ||
| 4080 | |||
| 4081 | /* Clear key, value, and hash. */ | ||
| 4082 | HASH_KEY (h, i) = HASH_VALUE (h, i) = Qnil; | ||
| 4083 | HASH_HASH (h, i) = Qnil; | ||
| 4084 | |||
| 4085 | h->count = make_number (XFASTINT (h->count) - 1); | ||
| 4086 | } | ||
| 4087 | } | ||
| 4088 | else | ||
| 4089 | { | ||
| 4090 | if (!remove_p) | ||
| 4091 | { | ||
| 4092 | /* Make sure key and value survive. */ | ||
| 4093 | mark_object (&HASH_KEY (h, i)); | ||
| 4094 | mark_object (&HASH_VALUE (h, i)); | ||
| 4095 | marked = 1; | ||
| 4096 | } | ||
| 4097 | } | ||
| 4098 | |||
| 4099 | idx = next; | ||
| 4100 | } | ||
| 4101 | } | ||
| 4102 | |||
| 4103 | return marked; | ||
| 4104 | } | ||
| 4105 | |||
| 4025 | /* Remove elements from weak hash tables that don't survive the | 4106 | /* Remove elements from weak hash tables that don't survive the |
| 4026 | current garbage collection. Remove weak tables that don't survive | 4107 | current garbage collection. Remove weak tables that don't survive |
| 4027 | from Vweak_hash_tables. Called from gc_sweep. */ | 4108 | from Vweak_hash_tables. Called from gc_sweep. */ |
| @@ -4030,8 +4111,29 @@ void | |||
| 4030 | sweep_weak_hash_tables () | 4111 | sweep_weak_hash_tables () |
| 4031 | { | 4112 | { |
| 4032 | Lisp_Object table; | 4113 | Lisp_Object table; |
| 4033 | struct Lisp_Hash_Table *h = 0, *prev; | 4114 | struct Lisp_Hash_Table *h, *prev; |
| 4115 | int marked; | ||
| 4116 | |||
| 4117 | /* Mark all keys and values that are in use. Keep on marking until | ||
| 4118 | there is no more change. This is necessary for cases like | ||
| 4119 | value-weak table A containing an entry X -> Y, where Y is used in a | ||
| 4120 | key-weak table B, Z -> Y. If B comes after A in the list of weak | ||
| 4121 | tables, X -> Y might be removed from A, although when looking at B | ||
| 4122 | one finds that it shouldn't. */ | ||
| 4123 | do | ||
| 4124 | { | ||
| 4125 | marked = 0; | ||
| 4126 | for (table = Vweak_hash_tables; !GC_NILP (table); table = h->next_weak) | ||
| 4127 | { | ||
| 4128 | h = XHASH_TABLE (table); | ||
| 4129 | if (h->size & ARRAY_MARK_FLAG) | ||
| 4130 | marked |= sweep_weak_table (h, 0); | ||
| 4131 | } | ||
| 4132 | } | ||
| 4133 | while (marked); | ||
| 4034 | 4134 | ||
| 4135 | /* Remove tables and entries that aren't used. */ | ||
| 4136 | prev = NULL; | ||
| 4035 | for (table = Vweak_hash_tables; !GC_NILP (table); table = h->next_weak) | 4137 | for (table = Vweak_hash_tables; !GC_NILP (table); table = h->next_weak) |
| 4036 | { | 4138 | { |
| 4037 | prev = h; | 4139 | prev = h; |
| @@ -4040,64 +4142,7 @@ sweep_weak_hash_tables () | |||
| 4040 | if (h->size & ARRAY_MARK_FLAG) | 4142 | if (h->size & ARRAY_MARK_FLAG) |
| 4041 | { | 4143 | { |
| 4042 | if (XFASTINT (h->count) > 0) | 4144 | if (XFASTINT (h->count) > 0) |
| 4043 | { | 4145 | sweep_weak_table (h, 1); |
| 4044 | int bucket, n; | ||
| 4045 | |||
| 4046 | n = XVECTOR (h->index)->size & ~ARRAY_MARK_FLAG; | ||
| 4047 | for (bucket = 0; bucket < n; ++bucket) | ||
| 4048 | { | ||
| 4049 | Lisp_Object idx, prev; | ||
| 4050 | |||
| 4051 | /* Follow collision chain, removing entries that | ||
| 4052 | don't survive this garbage collection. */ | ||
| 4053 | idx = HASH_INDEX (h, bucket); | ||
| 4054 | prev = Qnil; | ||
| 4055 | while (!GC_NILP (idx)) | ||
| 4056 | { | ||
| 4057 | int remove_p; | ||
| 4058 | int i = XFASTINT (idx); | ||
| 4059 | Lisp_Object next; | ||
| 4060 | |||
| 4061 | if (EQ (h->weak, Qkey)) | ||
| 4062 | remove_p = !survives_gc_p (HASH_KEY (h, i)); | ||
| 4063 | else if (EQ (h->weak, Qvalue)) | ||
| 4064 | remove_p = !survives_gc_p (HASH_VALUE (h, i)); | ||
| 4065 | else if (EQ (h->weak, Qt)) | ||
| 4066 | remove_p = (!survives_gc_p (HASH_KEY (h, i)) | ||
| 4067 | || !survives_gc_p (HASH_VALUE (h, i))); | ||
| 4068 | else | ||
| 4069 | abort (); | ||
| 4070 | |||
| 4071 | next = HASH_NEXT (h, i); | ||
| 4072 | if (remove_p) | ||
| 4073 | { | ||
| 4074 | /* Take out of collision chain. */ | ||
| 4075 | if (GC_NILP (prev)) | ||
| 4076 | HASH_INDEX (h, i) = next; | ||
| 4077 | else | ||
| 4078 | HASH_NEXT (h, XFASTINT (prev)) = next; | ||
| 4079 | |||
| 4080 | /* Add to free list. */ | ||
| 4081 | HASH_NEXT (h, i) = h->next_free; | ||
| 4082 | h->next_free = idx; | ||
| 4083 | |||
| 4084 | /* Clear key, value, and hash. */ | ||
| 4085 | HASH_KEY (h, i) = HASH_VALUE (h, i) = Qnil; | ||
| 4086 | HASH_HASH (h, i) = Qnil; | ||
| 4087 | |||
| 4088 | h->count = make_number (XFASTINT (h->count) - 1); | ||
| 4089 | } | ||
| 4090 | else | ||
| 4091 | { | ||
| 4092 | /* Make sure key and value survive. */ | ||
| 4093 | mark_object (&HASH_KEY (h, i)); | ||
| 4094 | mark_object (&HASH_VALUE (h, i)); | ||
| 4095 | } | ||
| 4096 | |||
| 4097 | idx = next; | ||
| 4098 | } | ||
| 4099 | } | ||
| 4100 | } | ||
| 4101 | } | 4146 | } |
| 4102 | else | 4147 | else |
| 4103 | { | 4148 | { |