Mercurial > hg > xemacs-beta
comparison src/gc.c @ 3092:141c2920ea48
[xemacs-hg @ 2005-11-25 01:41:31 by crestani]
Incremental Garbage Collector
author | crestani |
---|---|
date | Fri, 25 Nov 2005 01:42:08 +0000 |
parents | |
children | d674024a8674 |
comparison
equal
deleted
inserted
replaced
3091:c22d8984148c | 3092:141c2920ea48 |
---|---|
1 /* New incremental garbage collector for XEmacs. | |
2 Copyright (C) 2005 Marcus Crestani. | |
3 | |
4 This file is part of XEmacs. | |
5 | |
6 XEmacs is free software; you can redistribute it and/or modify it | |
7 under the terms of the GNU General Public License as published by the | |
8 Free Software Foundation; either version 2, or (at your option) any | |
9 later version. | |
10 | |
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with XEmacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 Boston, MA 02111-1307, USA. */ | |
20 | |
21 /* Synched up with: Not in FSF. */ | |
22 | |
23 #include <config.h> | |
24 #include "lisp.h" | |
25 | |
26 #include "backtrace.h" | |
27 #include "buffer.h" | |
28 #include "bytecode.h" | |
29 #include "chartab.h" | |
30 #include "console-stream.h" | |
31 #include "device.h" | |
32 #include "elhash.h" | |
33 #include "events.h" | |
34 #include "extents-impl.h" | |
35 #include "file-coding.h" | |
36 #include "frame-impl.h" | |
37 #include "gc.h" | |
38 #include "glyphs.h" | |
39 #include "opaque.h" | |
40 #include "lrecord.h" | |
41 #include "lstream.h" | |
42 #include "process.h" | |
43 #include "profile.h" | |
44 #include "redisplay.h" | |
45 #include "specifier.h" | |
46 #include "sysfile.h" | |
47 #include "sysdep.h" | |
48 #include "window.h" | |
49 #include "vdb.h" | |
50 | |
51 | |
52 #define GC_CONS_THRESHOLD 2000000 | |
53 #define GC_CONS_INCREMENTAL_THRESHOLD 200000 | |
54 #define GC_INCREMENTAL_TRAVERSAL_THRESHOLD 100000 | |
55 | |
56 /* Number of bytes of consing done since the last GC. */ | |
57 EMACS_INT consing_since_gc; | |
58 | |
59 /* Number of bytes of consing done since startup. */ | |
60 EMACS_UINT total_consing; | |
61 | |
62 /* Number of bytes of current allocated heap objects. */ | |
63 EMACS_INT total_gc_usage; | |
64 | |
65 /* If the above is set. */ | |
66 int total_gc_usage_set; | |
67 | |
68 /* Number of bytes of consing since gc before another gc should be done. */ | |
69 EMACS_INT gc_cons_threshold; | |
70 | |
71 /* Nonzero during gc */ | |
72 int gc_in_progress; | |
73 | |
74 /* Percentage of consing of total data size before another GC. */ | |
75 EMACS_INT gc_cons_percentage; | |
76 | |
77 #ifdef NEW_GC | |
78 /* Number of bytes of consing since gc before another cycle of the gc | |
79 should be done in incremental mode. */ | |
80 EMACS_INT gc_cons_incremental_threshold; | |
81 | |
82 /* Number of elements marked in one cycle of incremental GC. */ | |
83 EMACS_INT gc_incremental_traversal_threshold; | |
84 | |
85 /* Nonzero during write barrier */ | |
86 int write_barrier_enabled; | |
87 #endif /* NEW_GC */ | |
88 | |
89 | |
90 | |
91 #ifdef NEW_GC | |
92 /************************************************************************/ | |
93 /* Incremental State and Statistics */ | |
94 /************************************************************************/ | |
95 | |
96 enum gc_phase | |
97 { | |
98 NONE, | |
99 INIT_GC, | |
100 PUSH_ROOT_SET, | |
101 MARK, | |
102 REPUSH_ROOT_SET, | |
103 FINISH_MARK, | |
104 FINALIZE, | |
105 SWEEP, | |
106 FINISH_GC | |
107 }; | |
108 | |
109 #ifndef ERROR_CHECK_GC | |
110 struct | |
111 { | |
112 enum gc_phase phase; | |
113 } gc_state; | |
114 #else /* ERROR_CHECK_GC */ | |
115 enum gc_stat_id | |
116 { | |
117 GC_STAT_TOTAL, | |
118 GC_STAT_IN_LAST_GC, | |
119 GC_STAT_IN_THIS_GC, | |
120 GC_STAT_IN_LAST_CYCLE, | |
121 GC_STAT_IN_THIS_CYCLE, | |
122 GC_STAT_COUNT /* has to be last */ | |
123 }; | |
124 | |
125 struct | |
126 { | |
127 enum gc_phase phase; | |
128 EMACS_INT n_gc[GC_STAT_COUNT]; | |
129 EMACS_INT n_cycles[GC_STAT_COUNT]; | |
130 EMACS_INT enqueued[GC_STAT_COUNT]; | |
131 EMACS_INT dequeued[GC_STAT_COUNT]; | |
132 EMACS_INT repushed[GC_STAT_COUNT]; | |
133 EMACS_INT enqueued2[GC_STAT_COUNT]; | |
134 EMACS_INT dequeued2[GC_STAT_COUNT]; | |
135 EMACS_INT finalized[GC_STAT_COUNT]; | |
136 EMACS_INT freed[GC_STAT_COUNT]; | |
137 EMACS_INT explicitly_freed; | |
138 EMACS_INT explicitly_tried_freed; | |
139 } gc_state; | |
140 #endif /* ERROR_CHECK_GC */ | |
141 | |
142 #define GC_PHASE gc_state.phase | |
143 #define GC_SET_PHASE(p) GC_PHASE = p | |
144 | |
145 #ifdef ERROR_CHECK_GC | |
146 # define GC_STAT_START_NEW_GC gc_stat_start_new_gc () | |
147 # define GC_STAT_RESUME_GC gc_stat_resume_gc () | |
148 | |
149 #define GC_STAT_TICK(STAT) \ | |
150 gc_state.STAT[GC_STAT_TOTAL]++; \ | |
151 gc_state.STAT[GC_STAT_IN_THIS_GC]++; \ | |
152 gc_state.STAT[GC_STAT_IN_THIS_CYCLE]++ | |
153 | |
154 # define GC_STAT_ENQUEUED \ | |
155 if (GC_PHASE == REPUSH_ROOT_SET) \ | |
156 { \ | |
157 GC_STAT_TICK (enqueued2); \ | |
158 } \ | |
159 else \ | |
160 { \ | |
161 GC_STAT_TICK (enqueued); \ | |
162 } | |
163 | |
164 # define GC_STAT_DEQUEUED \ | |
165 if (gc_state.phase == REPUSH_ROOT_SET) \ | |
166 { \ | |
167 GC_STAT_TICK (dequeued2); \ | |
168 } \ | |
169 else \ | |
170 { \ | |
171 GC_STAT_TICK (dequeued); \ | |
172 } | |
173 # define GC_STAT_REPUSHED GC_STAT_TICK (repushed) | |
174 | |
175 #define GC_STAT_RESUME(stat) \ | |
176 gc_state.stat[GC_STAT_IN_LAST_CYCLE] = \ | |
177 gc_state.stat[GC_STAT_IN_THIS_CYCLE]; \ | |
178 gc_state.stat[GC_STAT_IN_THIS_CYCLE] = 0 | |
179 | |
180 #define GC_STAT_RESTART(stat) \ | |
181 gc_state.stat[GC_STAT_IN_LAST_GC] = \ | |
182 gc_state.stat[GC_STAT_IN_THIS_GC]; \ | |
183 gc_state.stat[GC_STAT_IN_THIS_GC] = 0; \ | |
184 GC_STAT_RESUME (stat) | |
185 | |
186 void | |
187 gc_stat_start_new_gc (void) | |
188 { | |
189 gc_state.n_gc[GC_STAT_TOTAL]++; | |
190 gc_state.n_cycles[GC_STAT_TOTAL]++; | |
191 gc_state.n_cycles[GC_STAT_IN_LAST_GC] = gc_state.n_cycles[GC_STAT_IN_THIS_GC]; | |
192 gc_state.n_cycles[GC_STAT_IN_THIS_GC] = 1; | |
193 | |
194 GC_STAT_RESTART (enqueued); | |
195 GC_STAT_RESTART (dequeued); | |
196 GC_STAT_RESTART (repushed); | |
197 GC_STAT_RESTART (finalized); | |
198 GC_STAT_RESTART (enqueued2); | |
199 GC_STAT_RESTART (dequeued2); | |
200 GC_STAT_RESTART (freed); | |
201 } | |
202 | |
203 void | |
204 gc_stat_resume_gc (void) | |
205 { | |
206 gc_state.n_cycles[GC_STAT_TOTAL]++; | |
207 gc_state.n_cycles[GC_STAT_IN_THIS_GC]++; | |
208 GC_STAT_RESUME (enqueued); | |
209 GC_STAT_RESUME (dequeued); | |
210 GC_STAT_RESUME (repushed); | |
211 GC_STAT_RESUME (finalized); | |
212 GC_STAT_RESUME (enqueued2); | |
213 GC_STAT_RESUME (dequeued2); | |
214 GC_STAT_RESUME (freed); | |
215 } | |
216 | |
217 void | |
218 gc_stat_finalized (void) | |
219 { | |
220 GC_STAT_TICK (finalized); | |
221 } | |
222 | |
223 void | |
224 gc_stat_freed (void) | |
225 { | |
226 GC_STAT_TICK (freed); | |
227 } | |
228 | |
229 void | |
230 gc_stat_explicitly_freed (void) | |
231 { | |
232 gc_state.explicitly_freed++; | |
233 } | |
234 | |
235 void | |
236 gc_stat_explicitly_tried_freed (void) | |
237 { | |
238 gc_state.explicitly_tried_freed++; | |
239 } | |
240 | |
241 #define GC_STAT_PRINT_ONE(stat) \ | |
242 printf (" | %9s %10d %10d %10d %10d %10d\n", \ | |
243 #stat, \ | |
244 (int) gc_state.stat[GC_STAT_TOTAL], \ | |
245 (int) gc_state.stat[GC_STAT_IN_LAST_GC], \ | |
246 (int) gc_state.stat[GC_STAT_IN_THIS_GC], \ | |
247 (int) gc_state.stat[GC_STAT_IN_LAST_CYCLE], \ | |
248 (int) gc_state.stat[GC_STAT_IN_THIS_CYCLE]) | |
249 | |
250 void | |
251 gc_stat_print_stats (void) | |
252 { | |
253 printf (" | PHASE %d TOTAL_GC %d\n", | |
254 (int) GC_PHASE, | |
255 (int) gc_state.n_gc[GC_STAT_TOTAL]); | |
256 printf (" | %9s %10s %10s %10s %10s %10s\n", | |
257 "stat", "total", "last_gc", "this_gc", | |
258 "last_cycle", "this_cycle"); | |
259 printf (" | %9s %10d %10d %10d \n", | |
260 "cycle", (int) gc_state.n_cycles[GC_STAT_TOTAL], | |
261 (int) gc_state.n_cycles[GC_STAT_IN_LAST_GC], | |
262 (int) gc_state.n_cycles[GC_STAT_IN_THIS_GC]); | |
263 | |
264 GC_STAT_PRINT_ONE (enqueued); | |
265 GC_STAT_PRINT_ONE (dequeued); | |
266 GC_STAT_PRINT_ONE (repushed); | |
267 GC_STAT_PRINT_ONE (enqueued2); | |
268 GC_STAT_PRINT_ONE (dequeued2); | |
269 GC_STAT_PRINT_ONE (finalized); | |
270 GC_STAT_PRINT_ONE (freed); | |
271 | |
272 printf (" | explicitly freed %d tried %d\n", | |
273 (int) gc_state.explicitly_freed, | |
274 (int) gc_state.explicitly_tried_freed); | |
275 } | |
276 | |
277 DEFUN("gc-stats", Fgc_stats, 0, 0 ,"", /* | |
278 Return statistics about garbage collection cycles in a property list. | |
279 */ | |
280 ()) | |
281 { | |
282 Lisp_Object pl = Qnil; | |
283 #define PL(name,value) \ | |
284 pl = cons3 (intern (name), make_int ((int) gc_state.value), pl) | |
285 | |
286 PL ("explicitly-tried-freed", explicitly_tried_freed); | |
287 PL ("explicitly-freed", explicitly_freed); | |
288 PL ("freed-in-this-cycle", freed[GC_STAT_IN_THIS_CYCLE]); | |
289 PL ("freed-in-this-gc", freed[GC_STAT_IN_THIS_GC]); | |
290 PL ("freed-in-last-cycle", freed[GC_STAT_IN_LAST_CYCLE]); | |
291 PL ("freed-in-last-gc", freed[GC_STAT_IN_LAST_GC]); | |
292 PL ("freed-total", freed[GC_STAT_TOTAL]); | |
293 PL ("finalized-in-this-cycle", finalized[GC_STAT_IN_THIS_CYCLE]); | |
294 PL ("finalized-in-this-gc", finalized[GC_STAT_IN_THIS_GC]); | |
295 PL ("finalized-in-last-cycle", finalized[GC_STAT_IN_LAST_CYCLE]); | |
296 PL ("finalized-in-last-gc", finalized[GC_STAT_IN_LAST_GC]); | |
297 PL ("finalized-total", finalized[GC_STAT_TOTAL]); | |
298 PL ("repushed-in-this-cycle", repushed[GC_STAT_IN_THIS_CYCLE]); | |
299 PL ("repushed-in-this-gc", repushed[GC_STAT_IN_THIS_GC]); | |
300 PL ("repushed-in-last-cycle", repushed[GC_STAT_IN_LAST_CYCLE]); | |
301 PL ("repushed-in-last-gc", repushed[GC_STAT_IN_LAST_GC]); | |
302 PL ("repushed-total", repushed[GC_STAT_TOTAL]); | |
303 PL ("dequeued2-in-this-cycle", dequeued2[GC_STAT_IN_THIS_CYCLE]); | |
304 PL ("dequeued2-in-this-gc", dequeued2[GC_STAT_IN_THIS_GC]); | |
305 PL ("dequeued2-in-last-cycle", dequeued2[GC_STAT_IN_LAST_CYCLE]); | |
306 PL ("dequeued2-in-last-gc", dequeued2[GC_STAT_IN_LAST_GC]); | |
307 PL ("dequeued2-total", dequeued2[GC_STAT_TOTAL]); | |
308 PL ("enqueued2-in-this-cycle", enqueued2[GC_STAT_IN_THIS_CYCLE]); | |
309 PL ("enqueued2-in-this-gc", enqueued2[GC_STAT_IN_THIS_GC]); | |
310 PL ("enqueued2-in-last-cycle", enqueued2[GC_STAT_IN_LAST_CYCLE]); | |
311 PL ("enqueued2-in-last-gc", enqueued2[GC_STAT_IN_LAST_GC]); | |
312 PL ("enqueued2-total", enqueued2[GC_STAT_TOTAL]); | |
313 PL ("dequeued-in-this-cycle", dequeued[GC_STAT_IN_THIS_CYCLE]); | |
314 PL ("dequeued-in-this-gc", dequeued[GC_STAT_IN_THIS_GC]); | |
315 PL ("dequeued-in-last-cycle", dequeued[GC_STAT_IN_LAST_CYCLE]); | |
316 PL ("dequeued-in-last-gc", dequeued[GC_STAT_IN_LAST_GC]); | |
317 PL ("dequeued-total", dequeued[GC_STAT_TOTAL]); | |
318 PL ("enqueued-in-this-cycle", enqueued[GC_STAT_IN_THIS_CYCLE]); | |
319 PL ("enqueued-in-this-gc", enqueued[GC_STAT_IN_THIS_GC]); | |
320 PL ("enqueued-in-last-cycle", enqueued[GC_STAT_IN_LAST_CYCLE]); | |
321 PL ("enqueued-in-last-gc", enqueued[GC_STAT_IN_LAST_GC]); | |
322 PL ("enqueued-total", enqueued[GC_STAT_TOTAL]); | |
323 PL ("n-cycles-in-this-gc", n_cycles[GC_STAT_IN_THIS_GC]); | |
324 PL ("n-cycles-in-last-gc", n_cycles[GC_STAT_IN_LAST_GC]); | |
325 PL ("n-cycles-total", n_cycles[GC_STAT_TOTAL]); | |
326 PL ("n-gc-total", n_gc[GC_STAT_TOTAL]); | |
327 PL ("phase", phase); | |
328 return pl; | |
329 } | |
330 #else /* not ERROR_CHECK_GC */ | |
331 # define GC_STAT_START_NEW_GC | |
332 # define GC_STAT_RESUME_GC | |
333 # define GC_STAT_ENQUEUED | |
334 # define GC_STAT_DEQUEUED | |
335 # define GC_STAT_REPUSHED | |
336 # define GC_STAT_REMOVED | |
337 #endif /* not ERROR_CHECK_GC */ | |
338 #endif /* NEW_GC */ | |
339 | |
340 | |
341 /************************************************************************/ | |
342 /* Recompute need to garbage collect */ | |
343 /************************************************************************/ | |
344 | |
345 int need_to_garbage_collect; | |
346 | |
347 #ifdef ERROR_CHECK_GC | |
348 int always_gc = 0; /* Debugging hack; equivalent to | |
349 (setq gc-cons-thresold -1) */ | |
350 #else | |
351 #define always_gc 0 | |
352 #endif | |
353 | |
354 /* True if it's time to garbage collect now. */ | |
355 void | |
356 recompute_need_to_garbage_collect (void) | |
357 { | |
358 if (always_gc) | |
359 need_to_garbage_collect = 1; | |
360 else | |
361 need_to_garbage_collect = | |
362 #ifdef NEW_GC | |
363 write_barrier_enabled ? | |
364 (consing_since_gc > gc_cons_incremental_threshold) : | |
365 #endif /* NEW_GC */ | |
366 (consing_since_gc > gc_cons_threshold | |
367 && | |
368 #if 0 /* #### implement this better */ | |
369 (100 * consing_since_gc) / total_data_usage () >= | |
370 gc_cons_percentage | |
371 #else | |
372 (!total_gc_usage_set || | |
373 (100 * consing_since_gc) / total_gc_usage >= | |
374 gc_cons_percentage) | |
375 #endif | |
376 ); | |
377 recompute_funcall_allocation_flag (); | |
378 } | |
379 | |
380 | |
381 | |
382 /************************************************************************/ | |
383 /* Mark Phase */ | |
384 /************************************************************************/ | |
385 | |
386 static const struct memory_description lisp_object_description_1[] = { | |
387 { XD_LISP_OBJECT, 0 }, | |
388 { XD_END } | |
389 }; | |
390 | |
391 const struct sized_memory_description lisp_object_description = { | |
392 sizeof (Lisp_Object), | |
393 lisp_object_description_1 | |
394 }; | |
395 | |
396 #if defined (USE_KKCC) || defined (PDUMP) | |
397 | |
398 /* This function extracts the value of a count variable described somewhere | |
399 else in the description. It is converted corresponding to the type */ | |
400 EMACS_INT | |
401 lispdesc_indirect_count_1 (EMACS_INT code, | |
402 const struct memory_description *idesc, | |
403 const void *idata) | |
404 { | |
405 EMACS_INT count; | |
406 const void *irdata; | |
407 | |
408 int line = XD_INDIRECT_VAL (code); | |
409 int delta = XD_INDIRECT_DELTA (code); | |
410 | |
411 irdata = ((char *) idata) + | |
412 lispdesc_indirect_count (idesc[line].offset, idesc, idata); | |
413 switch (idesc[line].type) | |
414 { | |
415 case XD_BYTECOUNT: | |
416 count = * (Bytecount *) irdata; | |
417 break; | |
418 case XD_ELEMCOUNT: | |
419 count = * (Elemcount *) irdata; | |
420 break; | |
421 case XD_HASHCODE: | |
422 count = * (Hashcode *) irdata; | |
423 break; | |
424 case XD_INT: | |
425 count = * (int *) irdata; | |
426 break; | |
427 case XD_LONG: | |
428 count = * (long *) irdata; | |
429 break; | |
430 default: | |
431 stderr_out ("Unsupported count type : %d (line = %d, code = %ld)\n", | |
432 idesc[line].type, line, (long) code); | |
433 #if defined(USE_KKCC) && defined(DEBUG_XEMACS) | |
434 if (gc_in_progress) | |
435 kkcc_backtrace (); | |
436 #endif | |
437 #ifdef PDUMP | |
438 if (in_pdump) | |
439 pdump_backtrace (); | |
440 #endif | |
441 count = 0; /* warning suppression */ | |
442 ABORT (); | |
443 } | |
444 count += delta; | |
445 return count; | |
446 } | |
447 | |
448 /* SDESC is a "description map" (basically, a list of offsets used for | |
449 successive indirections) and OBJ is the first object to indirect off of. | |
450 Return the description ultimately found. */ | |
451 | |
452 const struct sized_memory_description * | |
453 lispdesc_indirect_description_1 (const void *obj, | |
454 const struct sized_memory_description *sdesc) | |
455 { | |
456 int pos; | |
457 | |
458 for (pos = 0; sdesc[pos].size >= 0; pos++) | |
459 obj = * (const void **) ((const char *) obj + sdesc[pos].size); | |
460 | |
461 return (const struct sized_memory_description *) obj; | |
462 } | |
463 | |
464 /* Compute the size of the data at RDATA, described by a single entry | |
465 DESC1 in a description array. OBJ and DESC are used for | |
466 XD_INDIRECT references. */ | |
467 | |
468 static Bytecount | |
469 lispdesc_one_description_line_size (void *rdata, | |
470 const struct memory_description *desc1, | |
471 const void *obj, | |
472 const struct memory_description *desc) | |
473 { | |
474 union_switcheroo: | |
475 switch (desc1->type) | |
476 { | |
477 case XD_LISP_OBJECT_ARRAY: | |
478 { | |
479 EMACS_INT val = lispdesc_indirect_count (desc1->data1, desc, obj); | |
480 return (val * sizeof (Lisp_Object)); | |
481 } | |
482 case XD_LISP_OBJECT: | |
483 case XD_LO_LINK: | |
484 return sizeof (Lisp_Object); | |
485 case XD_OPAQUE_PTR: | |
486 return sizeof (void *); | |
487 #ifdef NEW_GC | |
488 case XD_LISP_OBJECT_BLOCK_PTR: | |
489 #endif /* NEW_GC */ | |
490 case XD_BLOCK_PTR: | |
491 { | |
492 EMACS_INT val = lispdesc_indirect_count (desc1->data1, desc, obj); | |
493 return val * sizeof (void *); | |
494 } | |
495 case XD_BLOCK_ARRAY: | |
496 { | |
497 EMACS_INT val = lispdesc_indirect_count (desc1->data1, desc, obj); | |
498 | |
499 return (val * | |
500 lispdesc_block_size | |
501 (rdata, | |
502 lispdesc_indirect_description (obj, desc1->data2.descr))); | |
503 } | |
504 case XD_OPAQUE_DATA_PTR: | |
505 return sizeof (void *); | |
506 case XD_UNION_DYNAMIC_SIZE: | |
507 { | |
508 /* If an explicit size was given in the first-level structure | |
509 description, use it; else compute size based on current union | |
510 constant. */ | |
511 const struct sized_memory_description *sdesc = | |
512 lispdesc_indirect_description (obj, desc1->data2.descr); | |
513 if (sdesc->size) | |
514 return sdesc->size; | |
515 else | |
516 { | |
517 desc1 = lispdesc_process_xd_union (desc1, desc, obj); | |
518 if (desc1) | |
519 goto union_switcheroo; | |
520 break; | |
521 } | |
522 } | |
523 case XD_UNION: | |
524 { | |
525 /* If an explicit size was given in the first-level structure | |
526 description, use it; else compute size based on maximum of all | |
527 possible structures. */ | |
528 const struct sized_memory_description *sdesc = | |
529 lispdesc_indirect_description (obj, desc1->data2.descr); | |
530 if (sdesc->size) | |
531 return sdesc->size; | |
532 else | |
533 { | |
534 int count; | |
535 Bytecount max_size = -1, size; | |
536 | |
537 desc1 = sdesc->description; | |
538 | |
539 for (count = 0; desc1[count].type != XD_END; count++) | |
540 { | |
541 size = lispdesc_one_description_line_size (rdata, | |
542 &desc1[count], | |
543 obj, desc); | |
544 if (size > max_size) | |
545 max_size = size; | |
546 } | |
547 return max_size; | |
548 } | |
549 } | |
550 case XD_ASCII_STRING: | |
551 return sizeof (void *); | |
552 case XD_DOC_STRING: | |
553 return sizeof (void *); | |
554 case XD_INT_RESET: | |
555 return sizeof (int); | |
556 case XD_BYTECOUNT: | |
557 return sizeof (Bytecount); | |
558 case XD_ELEMCOUNT: | |
559 return sizeof (Elemcount); | |
560 case XD_HASHCODE: | |
561 return sizeof (Hashcode); | |
562 case XD_INT: | |
563 return sizeof (int); | |
564 case XD_LONG: | |
565 return sizeof (long); | |
566 default: | |
567 stderr_out ("Unsupported dump type : %d\n", desc1->type); | |
568 ABORT (); | |
569 } | |
570 | |
571 return 0; | |
572 } | |
573 | |
574 | |
575 /* Return the size of the memory block (NOT necessarily a structure!) | |
576 described by SDESC and pointed to by OBJ. If SDESC records an | |
577 explicit size (i.e. non-zero), it is simply returned; otherwise, | |
578 the size is calculated by the maximum offset and the size of the | |
579 object at that offset, rounded up to the maximum alignment. In | |
580 this case, we may need the object, for example when retrieving an | |
581 "indirect count" of an inlined array (the count is not constant, | |
582 but is specified by one of the elements of the memory block). (It | |
583 is generally not a problem if we return an overly large size -- we | |
584 will simply end up reserving more space than necessary; but if the | |
585 size is too small we could be in serious trouble, in particular | |
586 with nested inlined structures, where there may be alignment | |
587 padding in the middle of a block. #### In fact there is an (at | |
588 least theoretical) problem with an overly large size -- we may | |
589 trigger a protection fault when reading from invalid memory. We | |
590 need to handle this -- perhaps in a stupid but dependable way, | |
591 i.e. by trapping SIGSEGV and SIGBUS.) */ | |
592 | |
593 Bytecount | |
594 lispdesc_block_size_1 (const void *obj, Bytecount size, | |
595 const struct memory_description *desc) | |
596 { | |
597 EMACS_INT max_offset = -1; | |
598 int max_offset_pos = -1; | |
599 int pos; | |
600 | |
601 if (size) | |
602 return size; | |
603 | |
604 for (pos = 0; desc[pos].type != XD_END; pos++) | |
605 { | |
606 EMACS_INT offset = lispdesc_indirect_count (desc[pos].offset, desc, obj); | |
607 if (offset == max_offset) | |
608 { | |
609 stderr_out ("Two relocatable elements at same offset?\n"); | |
610 ABORT (); | |
611 } | |
612 else if (offset > max_offset) | |
613 { | |
614 max_offset = offset; | |
615 max_offset_pos = pos; | |
616 } | |
617 } | |
618 | |
619 if (max_offset_pos < 0) | |
620 return 0; | |
621 | |
622 { | |
623 Bytecount size_at_max; | |
624 size_at_max = | |
625 lispdesc_one_description_line_size ((char *) obj + max_offset, | |
626 &desc[max_offset_pos], obj, desc); | |
627 | |
628 /* We have no way of knowing the required alignment for this structure, | |
629 so just make it maximally aligned. */ | |
630 return MAX_ALIGN_SIZE (max_offset + size_at_max); | |
631 } | |
632 } | |
633 #endif /* defined (USE_KKCC) || defined (PDUMP) */ | |
634 | |
635 #ifdef MC_ALLOC | |
636 #define GC_CHECK_NOT_FREE(lheader) \ | |
637 gc_checking_assert (! LRECORD_FREE_P (lheader)); | |
638 #else /* MC_ALLOC */ | |
639 #define GC_CHECK_NOT_FREE(lheader) \ | |
640 gc_checking_assert (! LRECORD_FREE_P (lheader)); \ | |
641 gc_checking_assert (LHEADER_IMPLEMENTATION (lheader)->basic_p || \ | |
642 ! ((struct old_lcrecord_header *) lheader)->free) | |
643 #endif /* MC_ALLOC */ | |
644 | |
645 #ifdef USE_KKCC | |
646 /* The following functions implement the new mark algorithm. | |
647 They mark objects according to their descriptions. They | |
648 are modeled on the corresponding pdumper procedures. */ | |
649 | |
650 #if 0 | |
651 # define KKCC_STACK_AS_QUEUE 1 | |
652 #endif | |
653 | |
654 #ifdef DEBUG_XEMACS | |
655 /* The backtrace for the KKCC mark functions. */ | |
656 #define KKCC_INIT_BT_STACK_SIZE 4096 | |
657 | |
658 typedef struct | |
659 { | |
660 void *obj; | |
661 const struct memory_description *desc; | |
662 int pos; | |
663 } kkcc_bt_stack_entry; | |
664 | |
665 static kkcc_bt_stack_entry *kkcc_bt; | |
666 static int kkcc_bt_stack_size; | |
667 static int kkcc_bt_depth = 0; | |
668 | |
669 static void | |
670 kkcc_bt_init (void) | |
671 { | |
672 kkcc_bt_depth = 0; | |
673 kkcc_bt_stack_size = KKCC_INIT_BT_STACK_SIZE; | |
674 kkcc_bt = (kkcc_bt_stack_entry *) | |
675 xmalloc_and_zero (kkcc_bt_stack_size * sizeof (kkcc_bt_stack_entry)); | |
676 if (!kkcc_bt) | |
677 { | |
678 stderr_out ("KKCC backtrace stack init failed for size %d\n", | |
679 kkcc_bt_stack_size); | |
680 ABORT (); | |
681 } | |
682 } | |
683 | |
684 void | |
685 kkcc_backtrace (void) | |
686 { | |
687 int i; | |
688 stderr_out ("KKCC mark stack backtrace :\n"); | |
689 for (i = kkcc_bt_depth - 1; i >= 0; i--) | |
690 { | |
691 Lisp_Object obj = wrap_pointer_1 (kkcc_bt[i].obj); | |
692 stderr_out (" [%d]", i); | |
693 if ((XRECORD_LHEADER (obj)->type >= lrecord_type_last_built_in_type) | |
694 || (!LRECORDP (obj)) | |
695 || (!XRECORD_LHEADER_IMPLEMENTATION (obj))) | |
696 { | |
697 stderr_out (" non Lisp Object"); | |
698 } | |
699 else | |
700 { | |
701 stderr_out (" %s", | |
702 XRECORD_LHEADER_IMPLEMENTATION (obj)->name); | |
703 } | |
704 stderr_out (" (addr: 0x%x, desc: 0x%x, ", | |
705 (int) kkcc_bt[i].obj, | |
706 (int) kkcc_bt[i].desc); | |
707 if (kkcc_bt[i].pos >= 0) | |
708 stderr_out ("pos: %d)\n", kkcc_bt[i].pos); | |
709 else | |
710 if (kkcc_bt[i].pos == -1) | |
711 stderr_out ("root set)\n"); | |
712 else if (kkcc_bt[i].pos == -2) | |
713 stderr_out ("dirty object)\n"); | |
714 } | |
715 } | |
716 | |
717 static void | |
718 kkcc_bt_stack_realloc (void) | |
719 { | |
720 kkcc_bt_stack_size *= 2; | |
721 kkcc_bt = (kkcc_bt_stack_entry *) | |
722 xrealloc (kkcc_bt, kkcc_bt_stack_size * sizeof (kkcc_bt_stack_entry)); | |
723 if (!kkcc_bt) | |
724 { | |
725 stderr_out ("KKCC backtrace stack realloc failed for size %d\n", | |
726 kkcc_bt_stack_size); | |
727 ABORT (); | |
728 } | |
729 } | |
730 | |
731 static void | |
732 kkcc_bt_free (void) | |
733 { | |
734 xfree_1 (kkcc_bt); | |
735 kkcc_bt = 0; | |
736 kkcc_bt_stack_size = 0; | |
737 } | |
738 | |
739 static void | |
740 kkcc_bt_push (void *obj, const struct memory_description *desc, | |
741 int level, int pos) | |
742 { | |
743 kkcc_bt_depth = level; | |
744 kkcc_bt[kkcc_bt_depth].obj = obj; | |
745 kkcc_bt[kkcc_bt_depth].desc = desc; | |
746 kkcc_bt[kkcc_bt_depth].pos = pos; | |
747 kkcc_bt_depth++; | |
748 if (kkcc_bt_depth >= kkcc_bt_stack_size) | |
749 kkcc_bt_stack_realloc (); | |
750 } | |
751 | |
752 #else /* not DEBUG_XEMACS */ | |
753 #define kkcc_bt_init() | |
754 #define kkcc_bt_push(obj, desc, level, pos) | |
755 #endif /* not DEBUG_XEMACS */ | |
756 | |
757 /* Object memory descriptions are in the lrecord_implementation structure. | |
758 But copying them to a parallel array is much more cache-friendly. */ | |
759 const struct memory_description *lrecord_memory_descriptions[countof (lrecord_implementations_table)]; | |
760 | |
761 /* the initial stack size in kkcc_gc_stack_entries */ | |
762 #define KKCC_INIT_GC_STACK_SIZE 16384 | |
763 | |
764 typedef struct | |
765 { | |
766 void *data; | |
767 const struct memory_description *desc; | |
768 #ifdef DEBUG_XEMACS | |
769 int level; | |
770 int pos; | |
771 #endif | |
772 } kkcc_gc_stack_entry; | |
773 | |
774 | |
775 static kkcc_gc_stack_entry *kkcc_gc_stack_ptr; | |
776 static int kkcc_gc_stack_front; | |
777 static int kkcc_gc_stack_rear; | |
778 static int kkcc_gc_stack_size; | |
779 | |
780 #define KKCC_INC(i) ((i + 1) % kkcc_gc_stack_size) | |
781 #define KKCC_INC2(i) ((i + 2) % kkcc_gc_stack_size) | |
782 | |
783 #define KKCC_GC_STACK_FULL (KKCC_INC2 (kkcc_gc_stack_rear) == kkcc_gc_stack_front) | |
784 #define KKCC_GC_STACK_EMPTY (KKCC_INC (kkcc_gc_stack_rear) == kkcc_gc_stack_front) | |
785 | |
786 static void | |
787 kkcc_gc_stack_init (void) | |
788 { | |
789 kkcc_gc_stack_size = KKCC_INIT_GC_STACK_SIZE; | |
790 kkcc_gc_stack_ptr = (kkcc_gc_stack_entry *) | |
791 xmalloc_and_zero (kkcc_gc_stack_size * sizeof (kkcc_gc_stack_entry)); | |
792 if (!kkcc_gc_stack_ptr) | |
793 { | |
794 stderr_out ("stack init failed for size %d\n", kkcc_gc_stack_size); | |
795 ABORT (); | |
796 } | |
797 kkcc_gc_stack_front = 0; | |
798 kkcc_gc_stack_rear = kkcc_gc_stack_size - 1; | |
799 } | |
800 | |
801 static void | |
802 kkcc_gc_stack_free (void) | |
803 { | |
804 xfree_1 (kkcc_gc_stack_ptr); | |
805 kkcc_gc_stack_ptr = 0; | |
806 kkcc_gc_stack_front = 0; | |
807 kkcc_gc_stack_rear = 0; | |
808 kkcc_gc_stack_size = 0; | |
809 } | |
810 | |
811 static void | |
812 kkcc_gc_stack_realloc (void) | |
813 { | |
814 kkcc_gc_stack_entry *old_ptr = kkcc_gc_stack_ptr; | |
815 int old_size = kkcc_gc_stack_size; | |
816 kkcc_gc_stack_size *= 2; | |
817 kkcc_gc_stack_ptr = (kkcc_gc_stack_entry *) | |
818 xmalloc_and_zero (kkcc_gc_stack_size * sizeof (kkcc_gc_stack_entry)); | |
819 if (!kkcc_gc_stack_ptr) | |
820 { | |
821 stderr_out ("stack realloc failed for size %d\n", kkcc_gc_stack_size); | |
822 ABORT (); | |
823 } | |
824 if (kkcc_gc_stack_rear >= kkcc_gc_stack_front) | |
825 { | |
826 int number_elements = kkcc_gc_stack_rear - kkcc_gc_stack_front + 1; | |
827 memcpy (kkcc_gc_stack_ptr, &old_ptr[kkcc_gc_stack_front], | |
828 number_elements * sizeof (kkcc_gc_stack_entry)); | |
829 kkcc_gc_stack_front = 0; | |
830 kkcc_gc_stack_rear = number_elements - 1; | |
831 } | |
832 else | |
833 { | |
834 int number_elements = old_size - kkcc_gc_stack_front; | |
835 memcpy (kkcc_gc_stack_ptr, &old_ptr[kkcc_gc_stack_front], | |
836 number_elements * sizeof (kkcc_gc_stack_entry)); | |
837 memcpy (&kkcc_gc_stack_ptr[number_elements], &old_ptr[0], | |
838 (kkcc_gc_stack_rear + 1) * sizeof (kkcc_gc_stack_entry)); | |
839 kkcc_gc_stack_front = 0; | |
840 kkcc_gc_stack_rear = kkcc_gc_stack_rear + number_elements; | |
841 } | |
842 xfree_1 (old_ptr); | |
843 } | |
844 | |
845 static void | |
846 #ifdef DEBUG_XEMACS | |
847 kkcc_gc_stack_push_1 (void *data, const struct memory_description *desc, | |
848 int level, int pos) | |
849 #else | |
850 kkcc_gc_stack_push_1 (void *data, const struct memory_description *desc) | |
851 #endif | |
852 { | |
853 #ifdef NEW_GC | |
854 GC_STAT_ENQUEUED; | |
855 #endif /* NEW_GC */ | |
856 if (KKCC_GC_STACK_FULL) | |
857 kkcc_gc_stack_realloc(); | |
858 kkcc_gc_stack_rear = KKCC_INC (kkcc_gc_stack_rear); | |
859 kkcc_gc_stack_ptr[kkcc_gc_stack_rear].data = data; | |
860 kkcc_gc_stack_ptr[kkcc_gc_stack_rear].desc = desc; | |
861 #ifdef DEBUG_XEMACS | |
862 kkcc_gc_stack_ptr[kkcc_gc_stack_rear].level = level; | |
863 kkcc_gc_stack_ptr[kkcc_gc_stack_rear].pos = pos; | |
864 #endif | |
865 } | |
866 | |
867 #ifdef DEBUG_XEMACS | |
868 #define kkcc_gc_stack_push(data, desc, level, pos) \ | |
869 kkcc_gc_stack_push_1 (data, desc, level, pos) | |
870 #else | |
871 #define kkcc_gc_stack_push(data, desc, level, pos) \ | |
872 kkcc_gc_stack_push_1 (data, desc) | |
873 #endif | |
874 | |
875 static kkcc_gc_stack_entry * | |
876 kkcc_gc_stack_pop (void) | |
877 { | |
878 if (KKCC_GC_STACK_EMPTY) | |
879 return 0; | |
880 #ifdef NEW_GC | |
881 GC_STAT_DEQUEUED; | |
882 #endif /* NEW_GC */ | |
883 #ifndef KKCC_STACK_AS_QUEUE | |
884 /* stack behaviour */ | |
885 return &kkcc_gc_stack_ptr[kkcc_gc_stack_rear--]; | |
886 #else | |
887 /* queue behaviour */ | |
888 { | |
889 int old_front = kkcc_gc_stack_front; | |
890 kkcc_gc_stack_front = KKCC_INC (kkcc_gc_stack_front); | |
891 return &kkcc_gc_stack_ptr[old_front]; | |
892 } | |
893 #endif | |
894 } | |
895 | |
896 void | |
897 #ifdef DEBUG_XEMACS | |
898 kkcc_gc_stack_push_lisp_object_1 (Lisp_Object obj, int level, int pos) | |
899 #else | |
900 kkcc_gc_stack_push_lisp_object_1 (Lisp_Object obj) | |
901 #endif | |
902 { | |
903 if (XTYPE (obj) == Lisp_Type_Record) | |
904 { | |
905 struct lrecord_header *lheader = XRECORD_LHEADER (obj); | |
906 const struct memory_description *desc; | |
907 GC_CHECK_LHEADER_INVARIANTS (lheader); | |
908 desc = RECORD_DESCRIPTION (lheader); | |
909 if (! MARKED_RECORD_HEADER_P (lheader)) | |
910 { | |
911 #ifdef NEW_GC | |
912 MARK_GREY (lheader); | |
913 #else /* not NEW_GC */ | |
914 MARK_RECORD_HEADER (lheader); | |
915 #endif /* not NEW_GC */ | |
916 kkcc_gc_stack_push ((void *) lheader, desc, level, pos); | |
917 } | |
918 } | |
919 } | |
920 | |
921 #ifdef NEW_GC | |
922 #ifdef DEBUG_XEMACS | |
923 #define kkcc_gc_stack_push_lisp_object(obj, level, pos) \ | |
924 kkcc_gc_stack_push_lisp_object_1 (obj, level, pos) | |
925 #else | |
926 #define kkcc_gc_stack_push_lisp_object(obj, level, pos) \ | |
927 kkcc_gc_stack_push_lisp_object_1 (obj) | |
928 #endif | |
929 | |
930 void | |
931 #ifdef DEBUG_XEMACS | |
932 kkcc_gc_stack_repush_dirty_object_1 (Lisp_Object obj, int level, int pos) | |
933 #else | |
934 kkcc_gc_stack_repush_dirty_object_1 (Lisp_Object obj) | |
935 #endif | |
936 { | |
937 if (XTYPE (obj) == Lisp_Type_Record) | |
938 { | |
939 struct lrecord_header *lheader = XRECORD_LHEADER (obj); | |
940 const struct memory_description *desc; | |
941 GC_STAT_REPUSHED; | |
942 GC_CHECK_LHEADER_INVARIANTS (lheader); | |
943 desc = RECORD_DESCRIPTION (lheader); | |
944 MARK_GREY (lheader); | |
945 kkcc_gc_stack_push ((void*) lheader, desc, level, pos); | |
946 } | |
947 } | |
948 #endif /* NEW_GC */ | |
949 | |
950 #ifdef ERROR_CHECK_GC | |
951 #define KKCC_DO_CHECK_FREE(obj, allow_free) \ | |
952 do \ | |
953 { \ | |
954 if (!allow_free && XTYPE (obj) == Lisp_Type_Record) \ | |
955 { \ | |
956 struct lrecord_header *lheader = XRECORD_LHEADER (obj); \ | |
957 GC_CHECK_NOT_FREE (lheader); \ | |
958 } \ | |
959 } while (0) | |
960 #else | |
961 #define KKCC_DO_CHECK_FREE(obj, allow_free) | |
962 #endif | |
963 | |
964 #ifdef ERROR_CHECK_GC | |
965 #ifdef DEBUG_XEMACS | |
966 static void | |
967 mark_object_maybe_checking_free_1 (Lisp_Object obj, int allow_free, | |
968 int level, int pos) | |
969 #else | |
970 static void | |
971 mark_object_maybe_checking_free_1 (Lisp_Object obj, int allow_free) | |
972 #endif | |
973 { | |
974 KKCC_DO_CHECK_FREE (obj, allow_free); | |
975 kkcc_gc_stack_push_lisp_object (obj, level, pos); | |
976 } | |
977 | |
978 #ifdef DEBUG_XEMACS | |
979 #define mark_object_maybe_checking_free(obj, allow_free, level, pos) \ | |
980 mark_object_maybe_checking_free_1 (obj, allow_free, level, pos) | |
981 #else | |
982 #define mark_object_maybe_checking_free(obj, allow_free, level, pos) \ | |
983 mark_object_maybe_checking_free_1 (obj, allow_free) | |
984 #endif | |
985 #else /* not ERROR_CHECK_GC */ | |
986 #define mark_object_maybe_checking_free(obj, allow_free, level, pos) \ | |
987 kkcc_gc_stack_push_lisp_object (obj, level, pos) | |
988 #endif /* not ERROR_CHECK_GC */ | |
989 | |
990 | |
991 /* This function loops all elements of a struct pointer and calls | |
992 mark_with_description with each element. */ | |
993 static void | |
994 #ifdef DEBUG_XEMACS | |
995 mark_struct_contents_1 (const void *data, | |
996 const struct sized_memory_description *sdesc, | |
997 int count, int level, int pos) | |
998 #else | |
999 mark_struct_contents_1 (const void *data, | |
1000 const struct sized_memory_description *sdesc, | |
1001 int count) | |
1002 #endif | |
1003 { | |
1004 int i; | |
1005 Bytecount elsize; | |
1006 elsize = lispdesc_block_size (data, sdesc); | |
1007 | |
1008 for (i = 0; i < count; i++) | |
1009 { | |
1010 kkcc_gc_stack_push (((char *) data) + elsize * i, sdesc->description, | |
1011 level, pos); | |
1012 } | |
1013 } | |
1014 | |
1015 #ifdef DEBUG_XEMACS | |
1016 #define mark_struct_contents(data, sdesc, count, level, pos) \ | |
1017 mark_struct_contents_1 (data, sdesc, count, level, pos) | |
1018 #else | |
1019 #define mark_struct_contents(data, sdesc, count, level, pos) \ | |
1020 mark_struct_contents_1 (data, sdesc, count) | |
1021 #endif | |
1022 | |
1023 | |
1024 #ifdef NEW_GC | |
1025 /* This function loops all elements of a struct pointer and calls | |
1026 mark_with_description with each element. */ | |
1027 static void | |
1028 #ifdef DEBUG_XEMACS | |
1029 mark_lisp_object_block_contents_1 (const void *data, | |
1030 const struct sized_memory_description *sdesc, | |
1031 int count, int level, int pos) | |
1032 #else | |
1033 mark_lisp_object_block_contents_1 (const void *data, | |
1034 const struct sized_memory_description *sdesc, | |
1035 int count) | |
1036 #endif | |
1037 { | |
1038 int i; | |
1039 Bytecount elsize; | |
1040 elsize = lispdesc_block_size (data, sdesc); | |
1041 | |
1042 for (i = 0; i < count; i++) | |
1043 { | |
1044 const Lisp_Object obj = wrap_pointer_1 (((char *) data) + elsize * i); | |
1045 if (XTYPE (obj) == Lisp_Type_Record) | |
1046 { | |
1047 struct lrecord_header *lheader = XRECORD_LHEADER (obj); | |
1048 const struct memory_description *desc; | |
1049 GC_CHECK_LHEADER_INVARIANTS (lheader); | |
1050 desc = sdesc->description; | |
1051 if (! MARKED_RECORD_HEADER_P (lheader)) | |
1052 { | |
1053 MARK_GREY (lheader); | |
1054 kkcc_gc_stack_push ((void *) lheader, desc, level, pos); | |
1055 } | |
1056 } | |
1057 } | |
1058 } | |
1059 | |
1060 #ifdef DEBUG_XEMACS | |
1061 #define mark_lisp_object_block_contents(data, sdesc, count, level, pos) \ | |
1062 mark_lisp_object_block_contents_1 (data, sdesc, count, level, pos) | |
1063 #else | |
1064 #define mark_lisp_object_block_contents(data, sdesc, count, level, pos) \ | |
1065 mark_lisp_object_block_contents_1 (data, sdesc, count) | |
1066 #endif | |
1067 #endif /* not NEW_GC */ | |
1068 | |
1069 /* This function implements the KKCC mark algorithm. | |
1070 Instead of calling mark_object, all the alive Lisp_Objects are pushed | |
1071 on the kkcc_gc_stack. This function processes all elements on the stack | |
1072 according to their descriptions. */ | |
1073 static void | |
1074 kkcc_marking ( | |
1075 #ifdef NEW_GC | |
1076 int cnt | |
1077 #else /* not NEW_GC */ | |
1078 int UNUSED(cnt) | |
1079 #endif /* not NEW_GC */ | |
1080 ) | |
1081 { | |
1082 kkcc_gc_stack_entry *stack_entry = 0; | |
1083 void *data = 0; | |
1084 const struct memory_description *desc = 0; | |
1085 int pos; | |
1086 #ifdef NEW_GC | |
1087 int count = cnt; | |
1088 #endif /* NEW_GC */ | |
1089 #ifdef DEBUG_XEMACS | |
1090 int level = 0; | |
1091 #endif | |
1092 | |
1093 while ((stack_entry = kkcc_gc_stack_pop ()) != 0) | |
1094 { | |
1095 data = stack_entry->data; | |
1096 desc = stack_entry->desc; | |
1097 #ifdef DEBUG_XEMACS | |
1098 level = stack_entry->level + 1; | |
1099 #endif | |
1100 kkcc_bt_push (data, desc, stack_entry->level, stack_entry->pos); | |
1101 | |
1102 #ifdef NEW_GC | |
1103 /* Mark black if object is currently grey. This first checks, | |
1104 if the object is really allocated on the mc-heap. If it is, | |
1105 it can be marked black; if it is not, it cannot be marked. */ | |
1106 maybe_mark_black (data); | |
1107 #endif /* NEW_GC */ | |
1108 | |
1109 if (!data) continue; | |
1110 | |
1111 gc_checking_assert (data); | |
1112 gc_checking_assert (desc); | |
1113 | |
1114 for (pos = 0; desc[pos].type != XD_END; pos++) | |
1115 { | |
1116 const struct memory_description *desc1 = &desc[pos]; | |
1117 const void *rdata = | |
1118 (const char *) data + lispdesc_indirect_count (desc1->offset, | |
1119 desc, data); | |
1120 union_switcheroo: | |
1121 | |
1122 /* If the flag says don't mark, then don't mark. */ | |
1123 if ((desc1->flags) & XD_FLAG_NO_KKCC) | |
1124 continue; | |
1125 | |
1126 switch (desc1->type) | |
1127 { | |
1128 case XD_BYTECOUNT: | |
1129 case XD_ELEMCOUNT: | |
1130 case XD_HASHCODE: | |
1131 case XD_INT: | |
1132 case XD_LONG: | |
1133 case XD_INT_RESET: | |
1134 case XD_LO_LINK: | |
1135 case XD_OPAQUE_PTR: | |
1136 case XD_OPAQUE_DATA_PTR: | |
1137 case XD_ASCII_STRING: | |
1138 case XD_DOC_STRING: | |
1139 break; | |
1140 case XD_LISP_OBJECT: | |
1141 { | |
1142 const Lisp_Object *stored_obj = (const Lisp_Object *) rdata; | |
1143 | |
1144 /* Because of the way that tagged objects work (pointers and | |
1145 Lisp_Objects have the same representation), XD_LISP_OBJECT | |
1146 can be used for untagged pointers. They might be NULL, | |
1147 though. */ | |
1148 if (EQ (*stored_obj, Qnull_pointer)) | |
1149 break; | |
1150 #ifdef MC_ALLOC | |
1151 mark_object_maybe_checking_free (*stored_obj, 0, level, pos); | |
1152 #else /* not MC_ALLOC */ | |
1153 mark_object_maybe_checking_free | |
1154 (*stored_obj, (desc1->flags) & XD_FLAG_FREE_LISP_OBJECT, | |
1155 level, pos); | |
1156 #endif /* not MC_ALLOC */ | |
1157 break; | |
1158 } | |
1159 case XD_LISP_OBJECT_ARRAY: | |
1160 { | |
1161 int i; | |
1162 EMACS_INT count = | |
1163 lispdesc_indirect_count (desc1->data1, desc, data); | |
1164 | |
1165 for (i = 0; i < count; i++) | |
1166 { | |
1167 const Lisp_Object *stored_obj = | |
1168 (const Lisp_Object *) rdata + i; | |
1169 | |
1170 if (EQ (*stored_obj, Qnull_pointer)) | |
1171 break; | |
1172 #ifdef MC_ALLOC | |
1173 mark_object_maybe_checking_free | |
1174 (*stored_obj, 0, level, pos); | |
1175 #else /* not MC_ALLOC */ | |
1176 mark_object_maybe_checking_free | |
1177 (*stored_obj, (desc1->flags) & XD_FLAG_FREE_LISP_OBJECT, | |
1178 level, pos); | |
1179 #endif /* not MC_ALLOC */ | |
1180 } | |
1181 break; | |
1182 } | |
1183 #ifdef NEW_GC | |
1184 case XD_LISP_OBJECT_BLOCK_PTR: | |
1185 { | |
1186 EMACS_INT count = lispdesc_indirect_count (desc1->data1, desc, | |
1187 data); | |
1188 const struct sized_memory_description *sdesc = | |
1189 lispdesc_indirect_description (data, desc1->data2.descr); | |
1190 const char *dobj = * (const char **) rdata; | |
1191 if (dobj) | |
1192 mark_lisp_object_block_contents | |
1193 (dobj, sdesc, count, level, pos); | |
1194 break; | |
1195 } | |
1196 #endif /* NEW_GC */ | |
1197 case XD_BLOCK_PTR: | |
1198 { | |
1199 EMACS_INT count = lispdesc_indirect_count (desc1->data1, desc, | |
1200 data); | |
1201 const struct sized_memory_description *sdesc = | |
1202 lispdesc_indirect_description (data, desc1->data2.descr); | |
1203 const char *dobj = * (const char **) rdata; | |
1204 if (dobj) | |
1205 mark_struct_contents (dobj, sdesc, count, level, pos); | |
1206 break; | |
1207 } | |
1208 case XD_BLOCK_ARRAY: | |
1209 { | |
1210 EMACS_INT count = lispdesc_indirect_count (desc1->data1, desc, | |
1211 data); | |
1212 const struct sized_memory_description *sdesc = | |
1213 lispdesc_indirect_description (data, desc1->data2.descr); | |
1214 | |
1215 mark_struct_contents (rdata, sdesc, count, level, pos); | |
1216 break; | |
1217 } | |
1218 case XD_UNION: | |
1219 case XD_UNION_DYNAMIC_SIZE: | |
1220 desc1 = lispdesc_process_xd_union (desc1, desc, data); | |
1221 if (desc1) | |
1222 goto union_switcheroo; | |
1223 break; | |
1224 | |
1225 default: | |
1226 stderr_out ("Unsupported description type : %d\n", desc1->type); | |
1227 kkcc_backtrace (); | |
1228 ABORT (); | |
1229 } | |
1230 } | |
1231 | |
1232 #ifdef NEW_GC | |
1233 if (cnt) | |
1234 if (!--count) | |
1235 break; | |
1236 #endif /* NEW_GC */ | |
1237 } | |
1238 } | |
1239 #endif /* USE_KKCC */ | |
1240 | |
1241 /* I hate duplicating all this crap! */ | |
1242 int | |
1243 marked_p (Lisp_Object obj) | |
1244 { | |
1245 /* Checks we used to perform. */ | |
1246 /* if (EQ (obj, Qnull_pointer)) return 1; */ | |
1247 /* if (!POINTER_TYPE_P (XGCTYPE (obj))) return 1; */ | |
1248 /* if (PURIFIED (XPNTR (obj))) return 1; */ | |
1249 | |
1250 if (XTYPE (obj) == Lisp_Type_Record) | |
1251 { | |
1252 struct lrecord_header *lheader = XRECORD_LHEADER (obj); | |
1253 | |
1254 GC_CHECK_LHEADER_INVARIANTS (lheader); | |
1255 | |
1256 return MARKED_RECORD_HEADER_P (lheader); | |
1257 } | |
1258 return 1; | |
1259 } | |
1260 | |
1261 | |
1262 /* Mark reference to a Lisp_Object. If the object referred to has not been | |
1263 seen yet, recursively mark all the references contained in it. */ | |
1264 void | |
1265 mark_object ( | |
1266 #ifdef USE_KKCC | |
1267 Lisp_Object UNUSED (obj) | |
1268 #else | |
1269 Lisp_Object obj | |
1270 #endif | |
1271 ) | |
1272 { | |
1273 #ifdef USE_KKCC | |
1274 /* this code should never be reached when configured for KKCC */ | |
1275 stderr_out ("KKCC: Invalid mark_object call.\n"); | |
1276 stderr_out ("Replace mark_object with kkcc_gc_stack_push_lisp_object.\n"); | |
1277 ABORT (); | |
1278 #else /* not USE_KKCC */ | |
1279 | |
1280 tail_recurse: | |
1281 | |
1282 /* Checks we used to perform */ | |
1283 /* if (EQ (obj, Qnull_pointer)) return; */ | |
1284 /* if (!POINTER_TYPE_P (XGCTYPE (obj))) return; */ | |
1285 /* if (PURIFIED (XPNTR (obj))) return; */ | |
1286 | |
1287 if (XTYPE (obj) == Lisp_Type_Record) | |
1288 { | |
1289 struct lrecord_header *lheader = XRECORD_LHEADER (obj); | |
1290 | |
1291 GC_CHECK_LHEADER_INVARIANTS (lheader); | |
1292 | |
1293 /* We handle this separately, above, so we can mark free objects */ | |
1294 GC_CHECK_NOT_FREE (lheader); | |
1295 | |
1296 /* All c_readonly objects have their mark bit set, | |
1297 so that we only need to check the mark bit here. */ | |
1298 if (! MARKED_RECORD_HEADER_P (lheader)) | |
1299 { | |
1300 MARK_RECORD_HEADER (lheader); | |
1301 | |
1302 if (RECORD_MARKER (lheader)) | |
1303 { | |
1304 obj = RECORD_MARKER (lheader) (obj); | |
1305 if (!NILP (obj)) goto tail_recurse; | |
1306 } | |
1307 } | |
1308 } | |
1309 #endif /* not KKCC */ | |
1310 } | |
1311 | |
1312 | |
1313 /************************************************************************/ | |
1314 /* Hooks */ | |
1315 /************************************************************************/ | |
1316 | |
1317 /* Nonzero when calling certain hooks or doing other things where a GC | |
1318 would be bad. It prevents infinite recursive calls to gc. */ | |
1319 int gc_currently_forbidden; | |
1320 | |
1321 int | |
1322 begin_gc_forbidden (void) | |
1323 { | |
1324 return internal_bind_int (&gc_currently_forbidden, 1); | |
1325 } | |
1326 | |
1327 void | |
1328 end_gc_forbidden (int count) | |
1329 { | |
1330 unbind_to (count); | |
1331 } | |
1332 | |
1333 /* Hooks. */ | |
1334 Lisp_Object Vpre_gc_hook, Qpre_gc_hook; | |
1335 Lisp_Object Vpost_gc_hook, Qpost_gc_hook; | |
1336 | |
1337 /* Maybe we want to use this when doing a "panic" gc after memory_full()? */ | |
1338 static int gc_hooks_inhibited; | |
1339 | |
1340 struct post_gc_action | |
1341 { | |
1342 void (*fun) (void *); | |
1343 void *arg; | |
1344 }; | |
1345 | |
1346 typedef struct post_gc_action post_gc_action; | |
1347 | |
1348 typedef struct | |
1349 { | |
1350 Dynarr_declare (post_gc_action); | |
1351 } post_gc_action_dynarr; | |
1352 | |
1353 static post_gc_action_dynarr *post_gc_actions; | |
1354 | |
1355 /* Register an action to be called at the end of GC. | |
1356 gc_in_progress is 0 when this is called. | |
1357 This is used when it is discovered that an action needs to be taken, | |
1358 but it's during GC, so it's not safe. (e.g. in a finalize method.) | |
1359 | |
1360 As a general rule, do not use Lisp objects here. | |
1361 And NEVER signal an error. | |
1362 */ | |
1363 | |
1364 void | |
1365 register_post_gc_action (void (*fun) (void *), void *arg) | |
1366 { | |
1367 post_gc_action action; | |
1368 | |
1369 if (!post_gc_actions) | |
1370 post_gc_actions = Dynarr_new (post_gc_action); | |
1371 | |
1372 action.fun = fun; | |
1373 action.arg = arg; | |
1374 | |
1375 Dynarr_add (post_gc_actions, action); | |
1376 } | |
1377 | |
1378 static void | |
1379 run_post_gc_actions (void) | |
1380 { | |
1381 int i; | |
1382 | |
1383 if (post_gc_actions) | |
1384 { | |
1385 for (i = 0; i < Dynarr_length (post_gc_actions); i++) | |
1386 { | |
1387 post_gc_action action = Dynarr_at (post_gc_actions, i); | |
1388 (action.fun) (action.arg); | |
1389 } | |
1390 | |
1391 Dynarr_reset (post_gc_actions); | |
1392 } | |
1393 } | |
1394 | |
1395 | |
1396 | |
1397 /************************************************************************/ | |
1398 /* Garbage Collection */ | |
1399 /************************************************************************/ | |
1400 | |
1401 /* Enable/disable incremental garbage collection during runtime. */ | |
1402 int allow_incremental_gc; | |
1403 | |
1404 /* For profiling. */ | |
1405 static Lisp_Object QSin_garbage_collection; | |
1406 | |
1407 /* Nonzero means display messages at beginning and end of GC. */ | |
1408 int garbage_collection_messages; | |
1409 | |
1410 /* "Garbage collecting" */ | |
1411 Lisp_Object Vgc_message; | |
1412 Lisp_Object Vgc_pointer_glyph; | |
1413 static const Ascbyte gc_default_message[] = "Garbage collecting"; | |
1414 Lisp_Object Qgarbage_collecting; | |
1415 | |
1416 /* "Locals" during GC. */ | |
1417 struct frame *f; | |
1418 int speccount; | |
1419 int cursor_changed; | |
1420 Lisp_Object pre_gc_cursor; | |
1421 | |
1422 /* PROFILE_DECLARE */ | |
1423 int do_backtrace; | |
1424 struct backtrace backtrace; | |
1425 | |
1426 /* Maximum amount of C stack to save when a GC happens. */ | |
1427 #ifndef MAX_SAVE_STACK | |
1428 #define MAX_SAVE_STACK 0 /* 16000 */ | |
1429 #endif | |
1430 | |
1431 void | |
1432 gc_prepare (void) | |
1433 { | |
1434 #if MAX_SAVE_STACK > 0 | |
1435 char stack_top_variable; | |
1436 extern char *stack_bottom; | |
1437 #endif | |
1438 | |
1439 #ifdef NEW_GC | |
1440 GC_STAT_START_NEW_GC; | |
1441 GC_SET_PHASE (INIT_GC); | |
1442 #endif /* NEW_GC */ | |
1443 | |
1444 do_backtrace = profiling_active || backtrace_with_internal_sections; | |
1445 | |
1446 assert (!gc_in_progress); | |
1447 assert (!in_display || gc_currently_forbidden); | |
1448 | |
1449 PROFILE_RECORD_ENTERING_SECTION (QSin_garbage_collection); | |
1450 | |
1451 /* We used to call selected_frame() here. | |
1452 | |
1453 The following functions cannot be called inside GC | |
1454 so we move to after the above tests. */ | |
1455 { | |
1456 Lisp_Object frame; | |
1457 Lisp_Object device = Fselected_device (Qnil); | |
1458 if (NILP (device)) /* Could happen during startup, eg. if always_gc */ | |
1459 return; | |
1460 frame = Fselected_frame (device); | |
1461 if (NILP (frame)) | |
1462 invalid_state ("No frames exist on device", device); | |
1463 f = XFRAME (frame); | |
1464 } | |
1465 | |
1466 pre_gc_cursor = Qnil; | |
1467 cursor_changed = 0; | |
1468 | |
1469 need_to_signal_post_gc = 0; | |
1470 recompute_funcall_allocation_flag (); | |
1471 | |
1472 if (!gc_hooks_inhibited) | |
1473 run_hook_trapping_problems | |
1474 (Qgarbage_collecting, Qpre_gc_hook, | |
1475 INHIBIT_EXISTING_PERMANENT_DISPLAY_OBJECT_DELETION); | |
1476 | |
1477 /* Now show the GC cursor/message. */ | |
1478 if (!noninteractive) | |
1479 { | |
1480 if (FRAME_WIN_P (f)) | |
1481 { | |
1482 Lisp_Object frame = wrap_frame (f); | |
1483 Lisp_Object cursor = glyph_image_instance (Vgc_pointer_glyph, | |
1484 FRAME_SELECTED_WINDOW (f), | |
1485 ERROR_ME_NOT, 1); | |
1486 pre_gc_cursor = f->pointer; | |
1487 if (POINTER_IMAGE_INSTANCEP (cursor) | |
1488 /* don't change if we don't know how to change back. */ | |
1489 && POINTER_IMAGE_INSTANCEP (pre_gc_cursor)) | |
1490 { | |
1491 cursor_changed = 1; | |
1492 Fset_frame_pointer (frame, cursor); | |
1493 } | |
1494 } | |
1495 | |
1496 /* Don't print messages to the stream device. */ | |
1497 if (!cursor_changed && !FRAME_STREAM_P (f)) | |
1498 { | |
1499 if (garbage_collection_messages) | |
1500 { | |
1501 Lisp_Object args[2], whole_msg; | |
1502 args[0] = (STRINGP (Vgc_message) ? Vgc_message : | |
1503 build_msg_string (gc_default_message)); | |
1504 args[1] = build_string ("..."); | |
1505 whole_msg = Fconcat (2, args); | |
1506 echo_area_message (f, (Ibyte *) 0, whole_msg, 0, -1, | |
1507 Qgarbage_collecting); | |
1508 } | |
1509 } | |
1510 } | |
1511 | |
1512 /***** Now we actually start the garbage collection. */ | |
1513 | |
1514 gc_in_progress = 1; | |
1515 #ifndef NEW_GC | |
1516 inhibit_non_essential_conversion_operations = 1; | |
1517 #endif /* NEW_GC */ | |
1518 | |
1519 #if MAX_SAVE_STACK > 0 | |
1520 | |
1521 /* Save a copy of the contents of the stack, for debugging. */ | |
1522 if (!purify_flag) | |
1523 { | |
1524 /* Static buffer in which we save a copy of the C stack at each GC. */ | |
1525 static char *stack_copy; | |
1526 static Bytecount stack_copy_size; | |
1527 | |
1528 ptrdiff_t stack_diff = &stack_top_variable - stack_bottom; | |
1529 Bytecount stack_size = (stack_diff > 0 ? stack_diff : -stack_diff); | |
1530 if (stack_size < MAX_SAVE_STACK) | |
1531 { | |
1532 if (stack_copy_size < stack_size) | |
1533 { | |
1534 stack_copy = (char *) xrealloc (stack_copy, stack_size); | |
1535 stack_copy_size = stack_size; | |
1536 } | |
1537 | |
1538 memcpy (stack_copy, | |
1539 stack_diff > 0 ? stack_bottom : &stack_top_variable, | |
1540 stack_size); | |
1541 } | |
1542 } | |
1543 #endif /* MAX_SAVE_STACK > 0 */ | |
1544 | |
1545 /* Do some totally ad-hoc resource clearing. */ | |
1546 /* #### generalize this? */ | |
1547 clear_event_resource (); | |
1548 cleanup_specifiers (); | |
1549 cleanup_buffer_undo_lists (); | |
1550 } | |
1551 | |
1552 void | |
1553 gc_mark_root_set ( | |
1554 #ifdef NEW_GC | |
1555 enum gc_phase phase | |
1556 #else /* not NEW_GC */ | |
1557 void | |
1558 #endif /* not NEW_GC */ | |
1559 ) | |
1560 { | |
1561 #ifdef NEW_GC | |
1562 GC_SET_PHASE (phase); | |
1563 #endif /* NEW_GC */ | |
1564 | |
1565 /* Mark all the special slots that serve as the roots of accessibility. */ | |
1566 | |
1567 #ifdef USE_KKCC | |
1568 # define mark_object(obj) kkcc_gc_stack_push_lisp_object (obj, 0, -1) | |
1569 #endif /* USE_KKCC */ | |
1570 | |
1571 { /* staticpro() */ | |
1572 Lisp_Object **p = Dynarr_begin (staticpros); | |
1573 Elemcount count; | |
1574 for (count = Dynarr_length (staticpros); count; count--) | |
1575 /* Need to check if the pointer in the staticpro array is not | |
1576 NULL. A gc can occur after variable is added to the staticpro | |
1577 array and _before_ it is correctly initialized. In this case | |
1578 its value is NULL, which we have to catch here. */ | |
1579 if (*p) | |
1580 mark_object (**p++); | |
1581 else | |
1582 **p++; | |
1583 } | |
1584 | |
1585 { /* staticpro_nodump() */ | |
1586 Lisp_Object **p = Dynarr_begin (staticpros_nodump); | |
1587 Elemcount count; | |
1588 for (count = Dynarr_length (staticpros_nodump); count; count--) | |
1589 /* Need to check if the pointer in the staticpro array is not | |
1590 NULL. A gc can occur after variable is added to the staticpro | |
1591 array and _before_ it is correctly initialized. In this case | |
1592 its value is NULL, which we have to catch here. */ | |
1593 if (*p) | |
1594 mark_object (**p++); | |
1595 else | |
1596 **p++; | |
1597 } | |
1598 | |
1599 #ifdef MC_ALLOC | |
1600 { /* mcpro () */ | |
1601 Lisp_Object *p = Dynarr_begin (mcpros); | |
1602 Elemcount count; | |
1603 for (count = Dynarr_length (mcpros); count; count--) | |
1604 mark_object (*p++); | |
1605 } | |
1606 #endif /* MC_ALLOC */ | |
1607 | |
1608 { /* GCPRO() */ | |
1609 struct gcpro *tail; | |
1610 int i; | |
1611 for (tail = gcprolist; tail; tail = tail->next) | |
1612 for (i = 0; i < tail->nvars; i++) | |
1613 mark_object (tail->var[i]); | |
1614 } | |
1615 | |
1616 { /* specbind() */ | |
1617 struct specbinding *bind; | |
1618 for (bind = specpdl; bind != specpdl_ptr; bind++) | |
1619 { | |
1620 mark_object (bind->symbol); | |
1621 mark_object (bind->old_value); | |
1622 } | |
1623 } | |
1624 | |
1625 { | |
1626 struct catchtag *c; | |
1627 for (c = catchlist; c; c = c->next) | |
1628 { | |
1629 mark_object (c->tag); | |
1630 mark_object (c->val); | |
1631 mark_object (c->actual_tag); | |
1632 mark_object (c->backtrace); | |
1633 } | |
1634 } | |
1635 | |
1636 { | |
1637 struct backtrace *backlist; | |
1638 for (backlist = backtrace_list; backlist; backlist = backlist->next) | |
1639 { | |
1640 int nargs = backlist->nargs; | |
1641 int i; | |
1642 | |
1643 mark_object (*backlist->function); | |
1644 if (nargs < 0 /* nargs == UNEVALLED || nargs == MANY */ | |
1645 /* might be fake (internal profiling entry) */ | |
1646 && backlist->args) | |
1647 mark_object (backlist->args[0]); | |
1648 else | |
1649 for (i = 0; i < nargs; i++) | |
1650 mark_object (backlist->args[i]); | |
1651 } | |
1652 } | |
1653 | |
1654 mark_profiling_info (); | |
1655 #ifdef USE_KKCC | |
1656 # undef mark_object | |
1657 #endif | |
1658 } | |
1659 | |
1660 void | |
1661 gc_finish_mark (void) | |
1662 { | |
1663 #ifdef NEW_GC | |
1664 GC_SET_PHASE (FINISH_MARK); | |
1665 #endif /* NEW_GC */ | |
1666 init_marking_ephemerons (); | |
1667 | |
1668 while (finish_marking_weak_hash_tables () > 0 || | |
1669 finish_marking_weak_lists () > 0 || | |
1670 continue_marking_ephemerons () > 0) | |
1671 #ifdef USE_KKCC | |
1672 { | |
1673 kkcc_marking (0); | |
1674 } | |
1675 #else /* not USE_KKCC */ | |
1676 ; | |
1677 #endif /* not USE_KKCC */ | |
1678 | |
1679 /* At this point, we know which objects need to be finalized: we | |
1680 still need to resurrect them */ | |
1681 | |
1682 while (finish_marking_ephemerons () > 0 || | |
1683 finish_marking_weak_lists () > 0 || | |
1684 finish_marking_weak_hash_tables () > 0) | |
1685 #ifdef USE_KKCC | |
1686 { | |
1687 kkcc_marking (0); | |
1688 } | |
1689 #else /* not USE_KKCC */ | |
1690 ; | |
1691 #endif /* not USE_KKCC */ | |
1692 | |
1693 /* And prune (this needs to be called after everything else has been | |
1694 marked and before we do any sweeping). */ | |
1695 /* #### this is somewhat ad-hoc and should probably be an object | |
1696 method */ | |
1697 prune_weak_hash_tables (); | |
1698 prune_weak_lists (); | |
1699 prune_specifiers (); | |
1700 prune_syntax_tables (); | |
1701 | |
1702 prune_ephemerons (); | |
1703 prune_weak_boxes (); | |
1704 } | |
1705 | |
1706 #ifdef NEW_GC | |
1707 void | |
1708 gc_finalize (void) | |
1709 { | |
1710 GC_SET_PHASE (FINALIZE); | |
1711 mc_finalize (); | |
1712 } | |
1713 | |
1714 void | |
1715 gc_sweep (void) | |
1716 { | |
1717 GC_SET_PHASE (SWEEP); | |
1718 mc_sweep (); | |
1719 } | |
1720 #endif /* NEW_GC */ | |
1721 | |
1722 | |
1723 void | |
1724 gc_finish (void) | |
1725 { | |
1726 #ifdef NEW_GC | |
1727 GC_SET_PHASE (FINISH_GC); | |
1728 #endif /* NEW_GC */ | |
1729 consing_since_gc = 0; | |
1730 #ifndef DEBUG_XEMACS | |
1731 /* Allow you to set it really fucking low if you really want ... */ | |
1732 if (gc_cons_threshold < 10000) | |
1733 gc_cons_threshold = 10000; | |
1734 #endif | |
1735 recompute_need_to_garbage_collect (); | |
1736 | |
1737 #ifndef NEW_GC | |
1738 inhibit_non_essential_conversion_operations = 0; | |
1739 #endif /* not NEW_GC */ | |
1740 gc_in_progress = 0; | |
1741 | |
1742 run_post_gc_actions (); | |
1743 | |
1744 /******* End of garbage collection ********/ | |
1745 | |
1746 /* Now remove the GC cursor/message */ | |
1747 if (!noninteractive) | |
1748 { | |
1749 if (cursor_changed) | |
1750 Fset_frame_pointer (wrap_frame (f), pre_gc_cursor); | |
1751 else if (!FRAME_STREAM_P (f)) | |
1752 { | |
1753 /* Show "...done" only if the echo area would otherwise be empty. */ | |
1754 if (NILP (clear_echo_area (selected_frame (), | |
1755 Qgarbage_collecting, 0))) | |
1756 { | |
1757 if (garbage_collection_messages) | |
1758 { | |
1759 Lisp_Object args[2], whole_msg; | |
1760 args[0] = (STRINGP (Vgc_message) ? Vgc_message : | |
1761 build_msg_string (gc_default_message)); | |
1762 args[1] = build_msg_string ("... done"); | |
1763 whole_msg = Fconcat (2, args); | |
1764 echo_area_message (selected_frame (), (Ibyte *) 0, | |
1765 whole_msg, 0, -1, | |
1766 Qgarbage_collecting); | |
1767 } | |
1768 } | |
1769 } | |
1770 } | |
1771 | |
1772 #ifndef MC_ALLOC | |
1773 if (!breathing_space) | |
1774 { | |
1775 breathing_space = malloc (4096 - MALLOC_OVERHEAD); | |
1776 } | |
1777 #endif /* not MC_ALLOC */ | |
1778 | |
1779 need_to_signal_post_gc = 1; | |
1780 funcall_allocation_flag = 1; | |
1781 | |
1782 PROFILE_RECORD_EXITING_SECTION (QSin_garbage_collection); | |
1783 | |
1784 #ifdef NEW_GC | |
1785 GC_SET_PHASE (NONE); | |
1786 #endif /* NEW_GC */ | |
1787 } | |
1788 | |
1789 #ifdef NEW_GC | |
1790 void | |
1791 gc_suspend_mark_phase (void) | |
1792 { | |
1793 PROFILE_RECORD_EXITING_SECTION (QSin_garbage_collection); | |
1794 write_barrier_enabled = 1; | |
1795 consing_since_gc = 0; | |
1796 vdb_start_dirty_bits_recording (); | |
1797 } | |
1798 | |
1799 int | |
1800 gc_resume_mark_phase (void) | |
1801 { | |
1802 PROFILE_RECORD_ENTERING_SECTION (QSin_garbage_collection); | |
1803 assert (write_barrier_enabled); | |
1804 vdb_stop_dirty_bits_recording (); | |
1805 write_barrier_enabled = 0; | |
1806 return vdb_read_dirty_bits (); | |
1807 } | |
1808 | |
1809 int | |
1810 gc_mark (int incremental) | |
1811 { | |
1812 GC_SET_PHASE (MARK); | |
1813 if (!incremental) | |
1814 { | |
1815 kkcc_marking (0); | |
1816 } | |
1817 else | |
1818 { | |
1819 kkcc_marking (gc_incremental_traversal_threshold); | |
1820 if (!KKCC_GC_STACK_EMPTY) | |
1821 { | |
1822 gc_suspend_mark_phase (); | |
1823 return 0; | |
1824 } | |
1825 } | |
1826 return 1; | |
1827 } | |
1828 | |
1829 int | |
1830 gc_resume_mark (int incremental) | |
1831 { | |
1832 if (!incremental) | |
1833 { | |
1834 if (!KKCC_GC_STACK_EMPTY) | |
1835 { | |
1836 GC_STAT_RESUME_GC; | |
1837 /* An incremental garbage collection is already running --- | |
1838 now wrap it up and resume it atomically. */ | |
1839 gc_resume_mark_phase (); | |
1840 gc_mark_root_set (REPUSH_ROOT_SET); | |
1841 kkcc_marking (0); | |
1842 } | |
1843 } | |
1844 else | |
1845 { | |
1846 int repushed_objects; | |
1847 int mark_work; | |
1848 GC_STAT_RESUME_GC; | |
1849 repushed_objects = gc_resume_mark_phase (); | |
1850 mark_work = (gc_incremental_traversal_threshold > repushed_objects) ? | |
1851 gc_incremental_traversal_threshold : repushed_objects; | |
1852 kkcc_marking (mark_work); | |
1853 if (KKCC_GC_STACK_EMPTY) | |
1854 { | |
1855 /* Mark root set again and finish up marking. */ | |
1856 gc_mark_root_set (REPUSH_ROOT_SET); | |
1857 kkcc_marking (0); | |
1858 } | |
1859 else | |
1860 { | |
1861 gc_suspend_mark_phase (); | |
1862 return 0; | |
1863 } | |
1864 } | |
1865 return 1; | |
1866 } | |
1867 | |
1868 | |
1869 void | |
1870 gc_1 (int incremental) | |
1871 { | |
1872 switch (GC_PHASE) | |
1873 { | |
1874 case NONE: | |
1875 gc_prepare (); | |
1876 kkcc_gc_stack_init(); | |
1877 #ifdef DEBUG_XEMACS | |
1878 kkcc_bt_init (); | |
1879 #endif | |
1880 case INIT_GC: | |
1881 gc_mark_root_set (PUSH_ROOT_SET); | |
1882 case PUSH_ROOT_SET: | |
1883 if (!gc_mark (incremental)) | |
1884 return; /* suspend gc */ | |
1885 case MARK: | |
1886 if (!KKCC_GC_STACK_EMPTY) | |
1887 if (!gc_resume_mark (incremental)) | |
1888 return; /* suspend gc */ | |
1889 gc_finish_mark (); | |
1890 kkcc_gc_stack_free (); | |
1891 #ifdef DEBUG_XEMACS | |
1892 kkcc_bt_free (); | |
1893 #endif | |
1894 case FINISH_MARK: | |
1895 gc_finalize (); | |
1896 case FINALIZE: | |
1897 gc_sweep (); | |
1898 case SWEEP: | |
1899 gc_finish (); | |
1900 case FINISH_GC: | |
1901 break; | |
1902 } | |
1903 } | |
1904 | |
1905 void gc (int incremental) | |
1906 { | |
1907 if (gc_currently_forbidden | |
1908 || in_display | |
1909 || preparing_for_armageddon) | |
1910 return; | |
1911 | |
1912 /* Very important to prevent GC during any of the following | |
1913 stuff that might run Lisp code; otherwise, we'll likely | |
1914 have infinite GC recursion. */ | |
1915 speccount = begin_gc_forbidden (); | |
1916 | |
1917 gc_1 (incremental); | |
1918 | |
1919 /* now stop inhibiting GC */ | |
1920 unbind_to (speccount); | |
1921 } | |
1922 | |
1923 void | |
1924 gc_full (void) | |
1925 { | |
1926 gc (0); | |
1927 } | |
1928 | |
1929 DEFUN ("gc-full", Fgc_full, 0, 0, "", /* | |
1930 This function performs a full garbage collection. If an incremental | |
1931 garbage collection is already running, it completes without any | |
1932 further interruption. This function guarantees that unused objects | |
1933 are freed when it returns. Garbage collection happens automatically if | |
1934 the client allocates more than `gc-cons-threshold' bytes of Lisp data | |
1935 since the previous garbage collection. | |
1936 */ | |
1937 ()) | |
1938 { | |
1939 gc_full (); | |
1940 return Qt; | |
1941 } | |
1942 | |
1943 void | |
1944 gc_incremental (void) | |
1945 { | |
1946 gc (allow_incremental_gc); | |
1947 } | |
1948 | |
1949 DEFUN ("gc-incremental", Fgc_incremental, 0, 0, "", /* | |
1950 This function starts an incremental garbage collection. If an | |
1951 incremental garbage collection is already running, the next cycle | |
1952 starts. Note that this function has not necessarily freed any memory | |
1953 when it returns. This function only guarantees, that the traversal of | |
1954 the heap makes progress. The next cycle of incremental garbage | |
1955 collection happens automatically if the client allocates more than | |
1956 `gc-incremental-cons-threshold' bytes of Lisp data since previous | |
1957 garbage collection. | |
1958 */ | |
1959 ()) | |
1960 { | |
1961 gc_incremental (); | |
1962 return Qt; | |
1963 } | |
1964 #else /* not NEW_GC */ | |
1965 void garbage_collect_1 (void) | |
1966 { | |
1967 if (gc_in_progress | |
1968 || gc_currently_forbidden | |
1969 || in_display | |
1970 || preparing_for_armageddon) | |
1971 return; | |
1972 | |
1973 /* Very important to prevent GC during any of the following | |
1974 stuff that might run Lisp code; otherwise, we'll likely | |
1975 have infinite GC recursion. */ | |
1976 speccount = begin_gc_forbidden (); | |
1977 | |
1978 gc_prepare (); | |
1979 #ifdef USE_KKCC | |
1980 kkcc_gc_stack_init(); | |
1981 #ifdef DEBUG_XEMACS | |
1982 kkcc_bt_init (); | |
1983 #endif | |
1984 #endif /* USE_KKCC */ | |
1985 gc_mark_root_set (); | |
1986 #ifdef USE_KKCC | |
1987 kkcc_marking (0); | |
1988 #endif /* USE_KKCC */ | |
1989 gc_finish_mark (); | |
1990 #ifdef USE_KKCC | |
1991 kkcc_gc_stack_free (); | |
1992 #ifdef DEBUG_XEMACS | |
1993 kkcc_bt_free (); | |
1994 #endif | |
1995 #endif /* USE_KKCC */ | |
1996 gc_sweep_1 (); | |
1997 gc_finish (); | |
1998 | |
1999 /* now stop inhibiting GC */ | |
2000 unbind_to (speccount); | |
2001 } | |
2002 #endif /* not NEW_GC */ | |
2003 | |
2004 | |
2005 /************************************************************************/ | |
2006 /* Initializations */ | |
2007 /************************************************************************/ | |
2008 | |
2009 /* Initialization */ | |
2010 static void | |
2011 common_init_gc_early (void) | |
2012 { | |
2013 Vgc_message = Qzero; | |
2014 | |
2015 gc_currently_forbidden = 0; | |
2016 gc_hooks_inhibited = 0; | |
2017 | |
2018 need_to_garbage_collect = always_gc; | |
2019 | |
2020 gc_cons_threshold = GC_CONS_THRESHOLD; | |
2021 gc_cons_percentage = 40; /* #### what is optimal? */ | |
2022 total_gc_usage_set = 0; | |
2023 #ifdef NEW_GC | |
2024 gc_cons_incremental_threshold = GC_CONS_INCREMENTAL_THRESHOLD; | |
2025 gc_incremental_traversal_threshold = GC_INCREMENTAL_TRAVERSAL_THRESHOLD; | |
2026 #endif /* not NEW_GC */ | |
2027 } | |
2028 | |
2029 void | |
2030 init_gc_early (void) | |
2031 { | |
2032 } | |
2033 | |
2034 void | |
2035 reinit_gc_early (void) | |
2036 { | |
2037 common_init_gc_early (); | |
2038 } | |
2039 | |
2040 void | |
2041 init_gc_once_early (void) | |
2042 { | |
2043 common_init_gc_early (); | |
2044 } | |
2045 | |
2046 void | |
2047 syms_of_gc (void) | |
2048 { | |
2049 DEFSYMBOL (Qpre_gc_hook); | |
2050 DEFSYMBOL (Qpost_gc_hook); | |
2051 #ifdef NEW_GC | |
2052 DEFSUBR (Fgc_full); | |
2053 DEFSUBR (Fgc_incremental); | |
2054 #ifdef ERROR_CHECK_GC | |
2055 DEFSUBR (Fgc_stats); | |
2056 #endif /* not ERROR_CHECK_GC */ | |
2057 #endif /* NEW_GC */ | |
2058 } | |
2059 | |
2060 void | |
2061 vars_of_gc (void) | |
2062 { | |
2063 staticpro_nodump (&pre_gc_cursor); | |
2064 | |
2065 QSin_garbage_collection = build_msg_string ("(in garbage collection)"); | |
2066 staticpro (&QSin_garbage_collection); | |
2067 | |
2068 DEFVAR_INT ("gc-cons-threshold", &gc_cons_threshold /* | |
2069 *Number of bytes of consing between full garbage collections. | |
2070 \"Consing\" is a misnomer in that this actually counts allocation | |
2071 of all different kinds of objects, not just conses. | |
2072 Garbage collection can happen automatically once this many bytes have been | |
2073 allocated since the last garbage collection. All data types count. | |
2074 | |
2075 Garbage collection happens automatically when `eval' or `funcall' are | |
2076 called. (Note that `funcall' is called implicitly as part of evaluation.) | |
2077 By binding this temporarily to a large number, you can effectively | |
2078 prevent garbage collection during a part of the program. | |
2079 | |
2080 Normally, you cannot set this value less than 10,000 (if you do, it is | |
2081 automatically reset during the next garbage collection). However, if | |
2082 XEmacs was compiled with DEBUG_XEMACS, this does not happen, allowing | |
2083 you to set this value very low to track down problems with insufficient | |
2084 GCPRO'ing. If you set this to a negative number, garbage collection will | |
2085 happen at *EVERY* call to `eval' or `funcall'. This is an extremely | |
2086 effective way to check GCPRO problems, but be warned that your XEmacs | |
2087 will be unusable! You almost certainly won't have the patience to wait | |
2088 long enough to be able to set it back. | |
2089 | |
2090 See also `consing-since-gc' and `gc-cons-percentage'. | |
2091 */ ); | |
2092 | |
2093 DEFVAR_INT ("gc-cons-percentage", &gc_cons_percentage /* | |
2094 *Percentage of memory allocated between garbage collections. | |
2095 | |
2096 Garbage collection will happen if this percentage of the total amount of | |
2097 memory used for data (see `lisp-object-memory-usage') has been allocated | |
2098 since the last garbage collection. However, it will not happen if less | |
2099 than `gc-cons-threshold' bytes have been allocated -- this sets an absolute | |
2100 minimum in case very little data has been allocated or the percentage is | |
2101 set very low. Set this to 0 to have garbage collection always happen after | |
2102 `gc-cons-threshold' bytes have been allocated, regardless of current memory | |
2103 usage. | |
2104 | |
2105 See also `consing-since-gc' and `gc-cons-threshold'. | |
2106 */ ); | |
2107 | |
2108 #ifdef NEW_GC | |
2109 DEFVAR_INT ("gc-cons-incremental-threshold", | |
2110 &gc_cons_incremental_threshold /* | |
2111 *Number of bytes of consing between cycles of incremental garbage | |
2112 collections. \"Consing\" is a misnomer in that this actually counts | |
2113 allocation of all different kinds of objects, not just conses. The | |
2114 next garbage collection cycle can happen automatically once this many | |
2115 bytes have been allocated since the last garbage collection cycle. | |
2116 All data types count. | |
2117 | |
2118 See also `gc-cons-threshold'. | |
2119 */ ); | |
2120 | |
2121 DEFVAR_INT ("gc-incremental-traversal-threshold", | |
2122 &gc_incremental_traversal_threshold /* | |
2123 *Number of elements processed in one cycle of incremental travesal. | |
2124 */ ); | |
2125 #endif /* NEW_GC */ | |
2126 | |
2127 DEFVAR_BOOL ("purify-flag", &purify_flag /* | |
2128 Non-nil means loading Lisp code in order to dump an executable. | |
2129 This means that certain objects should be allocated in readonly space. | |
2130 */ ); | |
2131 | |
2132 DEFVAR_BOOL ("garbage-collection-messages", &garbage_collection_messages /* | |
2133 Non-nil means display messages at start and end of garbage collection. | |
2134 */ ); | |
2135 garbage_collection_messages = 0; | |
2136 | |
2137 DEFVAR_LISP ("pre-gc-hook", &Vpre_gc_hook /* | |
2138 Function or functions to be run just before each garbage collection. | |
2139 Interrupts, garbage collection, and errors are inhibited while this hook | |
2140 runs, so be extremely careful in what you add here. In particular, avoid | |
2141 consing, and do not interact with the user. | |
2142 */ ); | |
2143 Vpre_gc_hook = Qnil; | |
2144 | |
2145 DEFVAR_LISP ("post-gc-hook", &Vpost_gc_hook /* | |
2146 Function or functions to be run just after each garbage collection. | |
2147 Interrupts, garbage collection, and errors are inhibited while this hook | |
2148 runs. Each hook is called with one argument which is an alist with | |
2149 finalization data. | |
2150 */ ); | |
2151 Vpost_gc_hook = Qnil; | |
2152 | |
2153 DEFVAR_LISP ("gc-message", &Vgc_message /* | |
2154 String to print to indicate that a garbage collection is in progress. | |
2155 This is printed in the echo area. If the selected frame is on a | |
2156 window system and `gc-pointer-glyph' specifies a value (i.e. a pointer | |
2157 image instance) in the domain of the selected frame, the mouse pointer | |
2158 will change instead of this message being printed. | |
2159 */ ); | |
2160 Vgc_message = build_string (gc_default_message); | |
2161 | |
2162 DEFVAR_LISP ("gc-pointer-glyph", &Vgc_pointer_glyph /* | |
2163 Pointer glyph used to indicate that a garbage collection is in progress. | |
2164 If the selected window is on a window system and this glyph specifies a | |
2165 value (i.e. a pointer image instance) in the domain of the selected | |
2166 window, the pointer will be changed as specified during garbage collection. | |
2167 Otherwise, a message will be printed in the echo area, as controlled | |
2168 by `gc-message'. | |
2169 */ ); | |
2170 | |
2171 #ifdef NEW_GC | |
2172 DEFVAR_BOOL ("allow-incremental-gc", &allow_incremental_gc /* | |
2173 *Non-nil means to allow incremental garbage collection. Nil prevents | |
2174 *incremental garbage collection, the garbage collector then only does | |
2175 *full collects (even if (gc-incremental) is called). | |
2176 */ ); | |
2177 #endif /* NEW_GC */ | |
2178 } | |
2179 | |
2180 void | |
2181 complex_vars_of_gc (void) | |
2182 { | |
2183 Vgc_pointer_glyph = Fmake_glyph_internal (Qpointer); | |
2184 } |