Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download

GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it

Path: gap4r8 / src / gasman.c
Views: 415067
1
/****************************************************************************
2
**
3
*W gasman.c GAP source Martin Schönert
4
**
5
**
6
*Y Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
7
*Y (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
8
*Y Copyright (C) 2002 The GAP Group
9
**
10
** This file contains the functions of Gasman, the GAP storage manager.
11
**
12
** {\Gasman} is a storage manager for applications written in C. That means
13
** that an application using {\Gasman} requests blocks of storage from
14
** {\Gasman}. Those blocks of storage are called *bags*. Then the
15
** application writes data into and reads data from the bags. Finally a bag
16
** is no longer needed and the application simply forgets it. We say that
17
** such a bag that is no longer needed is *dead*. {\Gasman} cares about the
18
** allocation of bags and deallocation of dead bags. Thus these operations
19
** are transparent to the application, enabling the programmer to
20
** concentrate on algorithms instead of caring about storage allocation and
21
** deallocation.
22
**
23
** {\Gasman} implements an automatic, cooperating, compacting, generational,
24
** conservative storage management
25
**
26
** *Automatic* means that the application only allocates bags. It need not
27
** explicitly deallocate them. {\Gasman} automatically determines which
28
** bags are dead and deallocates them. This is done by a process called
29
** *garbage collection*. Garbage refers to the bags that are dead, and
30
** collection refers to the process of deallocating them.
31
**
32
** *Cooperating* means that the application must cooperate with {\Gasman},
33
** that is it must follow two rules. One rule is that it must not remember
34
** the addresses of the data area of a bag for too long. The other rule is
35
** that it must inform {\Gasman} when it has changed a bag.
36
**
37
** *Compacting* means that after a garbage collection {\Gasman} compacts the
38
** bags that are still live, so that the storage made available by
39
** deallocating the dead bags becomes one large contiguous block. This
40
** helps to avoid *fragmentation* of the free storage. The downside is that
41
** the address of the data area of a bag may change during a garbage
42
** collection, which is the reason why the application must not remember
43
** this address for too long, i.e., must not keep pointers to or into the
44
** data area of a bag over a garbage collection.
45
**
46
** *Generational* means that {\Gasman} distinguishes between old and young
47
** bags. Old bags have been allocated some time ago, i.e., they survived at
48
** least one garbage collection. During a garbage collection {\Gasman} will
49
** first find and deallocate the dead young bags. Only if that does not
50
** produce enough free storage, {\Gasman} will find and deallocate the dead
51
** old bags. The idea behind this is that usually most bags have a very
52
** short life, so that they will die young. The downside is that this
53
** requires {\Gasman} to quickly find the young bags that are referenced
54
** from old bags, which is the reason why an application must inform
55
** {\Gasman} when it has changed a bag.
56
**
57
** *Conservative* means that there are situations in which {\Gasman} cannot
58
** decide with absolute certainty whether a bag is still live or already
59
** dead. In these situations {\Gasman} has to assume that the bag is still
60
** live, and may thus keep a bag longer than it is necessary.
61
**
62
** What follows describes the reasons for this design, and at the same time
63
** the assumptions that were made about the application. This is given so
64
** you can make an educated guess as to whether {\Gasman} is an appropriate
65
** storage manager for your application.
66
**
67
** {\Gasman} is automatic, because this makes it easier to use in large or
68
** complex applications. Namely in with a non-automatic storage manager the
69
** application must decide when to deallocate a bag. This requires in
70
** general global knowledge, i.e., it is not sufficient to know whether the
71
** current function may still need the bag, one also needs to know whether
72
** any other function may still need the bag. With growing size or
73
** complexity of the application it gets harder to obtain this knowledge.
74
**
75
** {\Gasman} is cooperating, because this is a requirement for compaction
76
** and generations (at least without cooperation, compaction and generations
77
** are very difficult). As described below, the former is important for
78
** storage efficiency, the latter for time efficiency. Note that the
79
** cooperation requires only local knowledge, i.e., whether or not a certain
80
** function of the application follows the two rules can be decided by just
81
** looking at the function without any knowledge about the rest of the
82
** application.
83
**
84
** {\Gasman} is compacting, because this allows the efficient usage of the
85
** available storage by applications where the ratio between the size of the
86
** smallest and the largest bag is large. Namely with a non-compacting
87
** storage manager, a part of the free storage may become unavailable
88
** because it is fragmented into many small pieces, each of which is too
89
** small to serve an allocation.
90
**
91
** {\Gasman} is generational, because this makes it very much faster, at
92
** least for those applications where most bags will indeed die young.
93
** Namely a non-generational storage manager must test for each bag whether
94
** or not it is still live during each garbage collection. However with
95
** many applications the probability that an old bag, i.e., one that
96
** survived at least one garbage collection, will also survive the next
97
** garbage collection is high. A generational storage manager simply
98
** assumes that each old bag is still live during most garbage collections.
99
** Thereby it avoids the expensive tests for most bags during most garbage
100
** collections.
101
**
102
** {\Gasman} is conservative, because for most applications only few bags
103
** are incorrectly assumed to be still live and the additional cooperation
104
** required to make {\Gasman} (more) precise would slow down the
105
** application. Note that the problem appears since the C compiler provides
106
** not enough information to distinguish between true references to bags and
107
** other values that just happen to look like references. Thus {\Gasman}
108
** has to assume that everything that could be interpreted as a reference to
109
** a bag is indeed such a reference, and that this bag is still live.
110
** Therefore some bags may be kept by {\Gasman}, even though they are
111
** already dead.
112
*/
113
#include <string.h>
114
#include <stdlib.h>
115
#include <stdio.h>
116
#include "system.h" /* Ints, UInts */
117
118
119
120
#include "gasman.h" /* garbage collector */
121
122
#ifdef BOEHM_GC
123
124
#define LARGE_GC_SIZE (8192 * sizeof(UInt))
125
#define TL_GC_SIZE (256 * sizeof(UInt))
126
127
#ifndef DISABLE_GC
128
#include <gc/gc.h>
129
#include <gc/gc_inline.h>
130
#include <gc/gc_typed.h>
131
#include <gc/gc_mark.h>
132
#else
133
#include <stdlib.h>
134
#endif
135
#endif
136
137
138
#include "objects.h" /* objects */
139
#include "scanner.h" /* scanner */
140
141
#include "code.h" /* coder */
142
#include "thread.h" /* threads */
143
#include "tls.h" /* thread-local storage */
144
#ifdef TRACK_CREATOR
145
/* Need CURR_FUNC and NAME_FUNC() */
146
#include "calls.h" /* calls */
147
#include "vars.h" /* variables */
148
#endif
149
150
151
152
/****************************************************************************
153
**
154
155
*F WORDS_BAG( <size> ) . . . . . . . . . . words used by a bag of given size
156
**
157
** The structure of a bag is a follows{\:}
158
**
159
** <identifier>
160
** __/
161
** /
162
** V
163
** +---------+
164
** |<masterp>|
165
** +---------+
166
** \____________
167
** \
168
** V
169
** +---------+---------+--------------------------------------------+----+
170
** |<sz>.<tp>| <link> | . . . . | pad|
171
** +---------+---------+--------------------------------------------+----+
172
**
173
** A bag consists of a masterpointer, and a body.
174
**
175
** The *masterpointer* is a pointer to the data area of the bag. During a
176
** garbage collection the masterpointer is the only active pointer to the
177
** data area of the bag, because of the rule that no pointers to or into the
178
** data area of a bag may be remembered over calls to functions that may
179
** cause a garbage collection. It is the job of the garbage collection to
180
** update the masterpointer of a bag when it moves the bag.
181
**
182
** The *identifier* of the bag is a pointer to (the address of) the
183
** masterpointer of the bag. Thus 'PTR_BAG(<bag>)' is simply '\*<bag>'
184
** plus a cast.
185
**
186
** The *body* of a bag consists of the size-type word, the link word, the
187
** data area, and the padding.
188
**
189
** The *size-type word* contains the size of the bag in the upper (at least
190
** 24) bits, and the type (abbreviated as <tp> in the above picture) in the
191
** lower 8 bits. Thus 'SIZE_BAG' simply extracts the size-type word and
192
** shifts it 8 bits to the right, and 'TNUM_BAG' extracts the size-type word
193
** and masks out everything except the lower 8 bits.
194
**
195
** The *link word* usually contains the identifier of the bag, i.e., a
196
** pointer to the masterpointer of the bag. Thus the garbage collection can
197
** find the masterpointer of a bag through the link word if it knows the
198
** address of the data area of the bag. The link word is also used by
199
** {\Gasman} to keep bags on two linked lists (see "ChangedBags" and
200
** "MarkedBags").
201
**
202
** The *data area* of a bag is the area that contains the data stored by
203
** the application in this bag.
204
**
205
** The *padding* consists of up to 'sizeof(Bag)-1' bytes and pads the body
206
** so that the size of a body is always a multiple of 'sizeof(Bag)'. This
207
** is to ensure that bags are always aligned. The macro 'WORDS_BAG(<size>)'
208
** returns the number of words occupied by the data area and padding of a
209
** bag of size <size>.
210
**
211
** A body in the workspace whose size-type word contains the value 255 in
212
** the lower 8 bits is the remainder of a 'ResizeBag'. That is it consists
213
** either of the unused words after a bag has been shrunk or of the old body
214
** of the bag after the contents of the body have been copied elsewhere for
215
** an extension. The upper (at least 24) bits of the first word contain the
216
** number of bytes in this area excluding the first word itself. Note that
217
** such a body has no link word, because such a remainder does not
218
** correspond to a bag (see "Implementation of ResizeBag").
219
**
220
** A masterpointer with a value congruent to 1 mod 4 is the relic of an
221
** object that was weakly but not strongly marked in a recent garbage
222
** collection. These persist until after the next full garbage collection
223
** by which time all references to them should have been removed.
224
**
225
*/
226
227
#define SIZE_MPTR_BAGS 1
228
#define WORDS_BAG(size) (((size) + (sizeof(Bag)-1)) / sizeof(Bag))
229
230
#ifdef USE_NEWSHAPE
231
#define HEADER_SIZE 2
232
#else
233
#define HEADER_SIZE 3
234
#endif
235
236
/* This could be 65536, but would waste memory in various tables */
237
238
#define NTYPES 256
239
240
/****************************************************************************
241
**
242
*V MptrBags . . . . . . . . . . . . . . beginning of the masterpointer area
243
*V OldBags . . . . . . . . . . . . . . . . . beginning of the old bags area
244
*V YoungBags . . . . . . . . . . . . . . . beginning of the young bags area
245
*V AllocBags . . . . . . . . . . . . . . . beginning of the allocation area
246
*V AllocSizeBags . . . . . . . . . . . . . . . . size of the allocation area
247
*V StopBags . . . . . . . . . . . . . . . beginning of the unavailable area
248
*V EndBags . . . . . . . . . . . . . . . . . . . . . . end of the workspace
249
**
250
** {\Gasman} manages one large block of storage called the *workspace*. The
251
** layout of the workspace is as follows{\:}
252
**
253
** +-------------+-----------------+------------+------------+-------------+
254
** |masterpointer| old bags | young bags | allocation | unavailable |
255
** | area | area | area | area | area |
256
** +-------------+-----------------+------------+------------+-------------+
257
** ^ ^ ^ ^ ^ ^
258
** MptrBags OldBags YoungBags AllocBags StopBags EndBags
259
**
260
** The *masterpointer area* contains all the masterpointers of the bags.
261
** 'MptrBags' points to the beginning of this area and 'OldBags' to the end.
262
**
263
** The *old bags area* contains the bodies of all the bags that survived at
264
** least one garbage collection. This area is only scanned for dead bags
265
** during a full garbage collection. 'OldBags' points to the beginning of
266
** this area and 'YoungBags' to the end.
267
**
268
** The *young bags area* contains the bodies of all the bags that have been
269
** allocated since the last garbage collection. This area is scanned for
270
** dead bags during each garbage collection. 'YoungBags' points to the
271
** beginning of this area and 'AllocBags' to the end.
272
**
273
** The *allocation area* is the storage that is available for allocation of
274
** new bags. When a new bag is allocated the storage for the body is taken
275
** from the beginning of this area, and this area is correspondingly
276
** reduced. If the body does not fit in the allocation area a garbage
277
** collection is performed. 'AllocBags' points to the beginning of this
278
** area and 'StopBags' to the end.
279
**
280
** The *unavailable area* is the free storage that is not available for
281
** allocation. 'StopBags' points to the beginning of this area and
282
** 'EndBags' to the end.
283
**
284
** If <cache-size> (see "InitBags") was 0, 'CollectBags' makes all of the
285
** free storage available for allocations by setting 'StopBags' to 'EndBags'
286
** after garbage collections. In this case garbage collections are only
287
** performed when no free storage is left. If <cache-size> was nonzero,
288
** 'CollectBags' makes 'AllocSizeBags' bytes available by setting 'StopBags'
289
** to 'AllocBags + 2+WORDS_BAG(<size>) + WORDS_BAG(AllocSizeBags)' after
290
** garbage collections, where <size> is the size of the bag 'NewBag' is
291
** currently allocating. 'AllocSizeBags' is usually <cache-size>, but is
292
** increased if only very few large bags have been allocated since the last
293
** garbage collection and decreased again if sufficiently many bags have
294
** been allocated since the last garbage collection. The idea is to keep
295
** the allocation area small enough so that it fits in the processor cache.
296
**
297
** Note that the borders between the areas are not static. In particular
298
** each allocation increases the size of the young bags area and reduces the
299
** size of the allocation area. On the other hand each garbage collection
300
** empties the young bags area.
301
*/
302
Bag * MptrBags;
303
Bag * OldBags;
304
Bag * YoungBags;
305
Bag * AllocBags;
306
UInt AllocSizeBags;
307
Bag * StopBags;
308
Bag * EndBags;
309
310
#if defined(MEMORY_CANARY) && !defined(BOEHM_GC)
311
312
#include <valgrind/valgrind.h>
313
#include <valgrind/memcheck.h>
314
Int canary_size() {
315
Int bufsize = (Int)StopBags - (Int)AllocBags;
316
return bufsize<4096?bufsize:4096;
317
}
318
319
void ADD_CANARY() {
320
VALGRIND_MAKE_MEM_NOACCESS(AllocBags, canary_size());
321
}
322
void CLEAR_CANARY() {
323
VALGRIND_MAKE_MEM_DEFINED(AllocBags, canary_size());
324
}
325
#define CANARY_DISABLE_VALGRIND() VALGRIND_DISABLE_ERROR_REPORTING
326
#define CANARY_ENABLE_VALGRIND() VALGRIND_ENABLE_ERROR_REPORTING
327
328
void CHANGED_BAG_IMPL(Bag bag) {
329
CANARY_DISABLE_VALGRIND();
330
if ( PTR_BAG(bag) <= YoungBags && PTR_BAG(bag)[-1] == (bag) ) {
331
PTR_BAG(bag)[-1] = ChangedBags;
332
ChangedBags = (bag);
333
}
334
CANARY_ENABLE_VALGRIND();
335
}
336
#else
337
#define ADD_CANARY()
338
#define CLEAR_CANARY()
339
#define CANARY_DISABLE_VALGRIND()
340
#define CANARY_ENABLE_VALGRIND()
341
#endif
342
343
344
/* These macros, are (a) for more readable code, but more importantly
345
(b) to ensure that unsigned subtracts and divides are used (since
346
we know the ordering of the pointers. This is needed on > 2GB
347
workspaces on 32 but systems. The Size****Area functions return an
348
answer in units of a word (ie sizeof(UInt) bytes), which should
349
therefore be small enough not to cause problems. */
350
351
#define SpaceBetweenPointers(a,b) (((UInt)((UInt)(a) - (UInt)(b)))/sizeof(Bag))
352
353
#define SizeMptrsArea SpaceBetweenPointers(OldBags, MptrBags)
354
#define SizeOldBagsArea SpaceBetweenPointers(YoungBags,OldBags)
355
#define SizeYoungBagsArea SpaceBetweenPointers(AllocBags, YoungBags)
356
#define SizeAllocationArea SpaceBetweenPointers(StopBags, AllocBags)
357
#define SizeUnavailableArea SpaceBetweenPointers(EndBags, StopBags)
358
359
#define SizeAllBagsArea SpaceBetweenPointers(AllocBags, OldBags)
360
#define SizeWorkspace SpaceBetweenPointers(EndBags, MptrBags)
361
362
/****************************************************************************
363
**
364
*V FreeMptrBags . . . . . . . . . . . . . . . list of free bag identifiers
365
**
366
** 'FreeMptrBags' is the first free bag identifier, i.e., it points to the
367
** first available masterpointer. If 'FreeMptrBags' is 0 there are no
368
** available masterpointers. The available masterpointers are managed in a
369
** forward linked list, i.e., each available masterpointer points to the
370
** next available masterpointer, except for the last, which contains 0.
371
**
372
** When a new bag is allocated it gets the identifier 'FreeMptrBags', and
373
** 'FreeMptrBags' is set to the value stored in this masterpointer, which is
374
** the next available masterpointer. When a bag is deallocated by a garbage
375
** collection its masterpointer is added to the list of available
376
** masterpointers again.
377
*/
378
Bag FreeMptrBags;
379
380
381
/****************************************************************************
382
**
383
*V ChangedBags . . . . . . . . . . . . . . . . . . list of changed old bags
384
**
385
** 'ChangedBags' holds a list of old bags that have been changed since the
386
** last garbage collection, i.e., for which either 'CHANGED_BAG' was called
387
** or which have been resized.
388
**
389
** This list starts with the bag whose identifier is 'ChangedBags', and the
390
** link word of each bag on the list contains the identifier of the next bag
391
** on the list. The link word of the last bag on the list contains 0. If
392
** 'ChangedBags' has the value 0, the list is empty.
393
**
394
** The garbage collection needs to know which young bags are subbags of old
395
** bags, since it must not throw those away in a partial garbage
396
** collection. Only those old bags that have been changed since the last
397
** garbage collection can contain references to young bags, which have been
398
** allocated since the last garbage collection. The application cooperates
399
** by informing {\Gasman} with 'CHANGED_BAG' which bags it has changed. The
400
** list of changed old bags is scanned by a partial garbage collection and
401
** the young subbags of the old bags on this list are marked with 'MARK_BAG'
402
** (see "MarkedBags"). Without this list 'CollectBags' would have to scan
403
** all old bags for references to young bags, which would take too much time
404
** (see "Implementation of CollectBags").
405
**
406
** 'CHANGED_BAG' puts a bag on the list of changed old bags. 'CHANGED_BAG'
407
** first checks that <bag> is an old bag by checking that 'PTR_BAG( <bag> )'
408
** is smaller than 'YoungBags'. Then it checks that the bag is not already
409
** on the list of changed bags by checking that the link word still contains
410
** the identifier of <bag>. If <bag> is an old bag that is not already on
411
** the list of changed bags, 'CHANGED_BAG' puts <bag> on the list of changed
412
** bags, by setting the link word of <bag> to the current value of
413
** 'ChangedBags' and then setting 'ChangedBags' to <bag>.
414
*/
415
Bag ChangedBags;
416
417
418
/****************************************************************************
419
**
420
*V MarkedBags . . . . . . . . . . . . . . . . . . . . . list of marked bags
421
**
422
** 'MarkedBags' holds a list of bags that have already been marked during a
423
** garbage collection by 'MARK_BAG'. This list is only used during garbage
424
** collections, so it is always empty outside of garbage collections (see
425
** "Implementation of CollectBags").
426
**
427
** This list starts with the bag whose identifier is 'MarkedBags', and the
428
** link word of each bag on the list contains the identifier of the next bag
429
** on the list. The link word of the last bag on the list contains 0. If
430
** 'MarkedBags' has the value 0, the list is empty.
431
**
432
** Note that some other storage managers do not use such a list during the
433
** mark phase. Instead they simply let the marking functions call each
434
** other. While this is somewhat simpler it may use an unbound amount of
435
** space on the stack. This is particularly bad on systems where the stack
436
** is not in a separate segment of the address space, and thus may grow into
437
** the workspace, causing disaster.
438
**
439
** 'MARK_BAG' puts a bag <bag> onto this list. 'MARK_BAG' has to be
440
** careful, because it can be called with an argument that is not really a
441
** bag identifier, and may point outside the programs address space. So
442
** 'MARK_BAG' first checks that <bag> points to a properly aligned location
443
** between 'MptrBags' and 'OldBags'. Then 'MARK_BAG' checks that <bag> is
444
** the identifier of a young bag by checking that the masterpointer points
445
** to a location between 'YoungBags' and 'AllocBags' (if <bag> is the
446
** identifier of an old bag, the masterpointer will point to a location
447
** between 'OldBags' and 'YoungBags', and if <bag> only appears to be an
448
** identifier, the masterpointer could be on the free list of masterpointers
449
** and point to a location between 'MptrBags' and 'OldBags'). Then
450
** 'MARK_BAG' checks that <bag> is not already marked by checking that the
451
** link word of <bag> contains the identifier of the bag. If any of the
452
** checks fails, 'MARK_BAG' does nothing. If all checks succeed, 'MARK_BAG'
453
** puts <bag> onto the list of marked bags by putting the current value of
454
** 'ChangedBags' into the link word of <bag> and setting 'ChangedBags' to
455
** <bag>. Note that since bags are always placed at the front of the list,
456
** 'CollectBags' will mark the bags in a depth-first order. This is
457
** probably good to improve the locality of reference.
458
*/
459
Bag MarkedBags;
460
461
462
/****************************************************************************
463
**
464
*V NrAllBags . . . . . . . . . . . . . . . . . number of all bags allocated
465
*V SizeAllBags . . . . . . . . . . . . . . total size of all bags allocated
466
*V NrLiveBags . . . . . . . . . . number of bags that survived the last gc
467
*V SizeLiveBags . . . . . . . total size of bags that survived the last gc
468
*V NrDeadBags . . . . . . . number of bags that died since the last full gc
469
*V SizeDeadBags . . . . total size of bags that died since the last full gc
470
*V NrHalfDeadBags . . . . . number of bags that died since the last full gc
471
** but may still be weakly pointed to
472
*/
473
UInt NrAllBags;
474
UInt SizeAllBags;
475
UInt NrLiveBags;
476
UInt SizeLiveBags;
477
UInt NrDeadBags;
478
UInt SizeDeadBags;
479
UInt NrHalfDeadBags;
480
481
/****************************************************************************
482
**
483
*V InfoBags[<type>] . . . . . . . . . . . . . . . . . information for bags
484
*/
485
TNumInfoBags InfoBags [ NTYPES ];
486
487
/****************************************************************************
488
**
489
*F IS_BAG -- check if a value looks like a masterpointer reference.
490
*/
491
static inline UInt IS_BAG (
492
UInt bid )
493
{
494
return (((UInt)MptrBags <= bid)
495
&& (bid < (UInt)OldBags)
496
&& (bid & (sizeof(Bag)-1)) == 0);
497
}
498
499
/****************************************************************************
500
**
501
*F InitMsgsFuncBags(<msgs-func>) . . . . . . . . . install message function
502
**
503
** 'InitMsgsFuncBags' simply stores the printing function in a global
504
** variable.
505
*/
506
TNumMsgsFuncBags MsgsFuncBags;
507
508
void InitMsgsFuncBags (
509
TNumMsgsFuncBags msgs_func )
510
{
511
#ifndef BOEHM_GC
512
MsgsFuncBags = msgs_func;
513
#endif
514
}
515
516
517
/****************************************************************************
518
**
519
*F InitSweepFuncBags(<type>,<mark-func>) . . . . install sweeping function
520
*/
521
522
TNumSweepFuncBags TabSweepFuncBags [ NTYPES ];
523
524
525
void InitSweepFuncBags (
526
UInt type,
527
TNumSweepFuncBags sweep_func )
528
{
529
#ifndef BOEHM_GC
530
#ifdef CHECK_FOR_CLASH_IN_INIT_SWEEP_FUNC
531
char str[256];
532
533
if ( TabSweepFuncBags[type] != 0 ) {
534
str[0] = 0;
535
strncat( str, "warning: sweep function for type ", 33 );
536
str[33] = '0' + ((type/100) % 10);
537
str[34] = '0' + ((type/ 10) % 10);
538
str[35] = '0' + ((type/ 1) % 10);
539
str[36] = 0;
540
strncat( str, " already installed\n", 19 );
541
SyFputs( str, 0 );
542
}
543
#endif
544
TabSweepFuncBags[type] = sweep_func;
545
#endif
546
}
547
548
#if ITANIUM
549
extern void * ItaniumRegisterStackTop();
550
551
static Bag* ItaniumRegisterStackBottom = (Bag *)0;
552
553
static void ItaniumSpecialMarkingInit() {
554
ItaniumRegisterStackBottom = (Bag *)ItaniumRegisterStackTop();
555
}
556
557
#endif
558
559
/****************************************************************************
560
**
561
*F InitMarkFuncBags(<type>,<mark-func>) . . . . . install marking function
562
*F MarkNoSubBags(<bag>) . . . . . . . . marking function that marks nothing
563
*F MarkOneSubBags(<bag>) . . . . . . marking function that marks one subbag
564
*F MarkTwoSubBags(<bag>) . . . . . . marking function that marks two subbags
565
*F MarkThreeSubBags(<bag>) . . . . marking function that marks three subbags
566
*F MarkFourSubBags(<bag>) . . . . marking function that marks four subbags
567
*F MarkAllSubBags(<bag>) . . . . . . marking function that marks everything
568
**
569
** 'InitMarkFuncBags', 'MarkNoSubBags', 'MarkOneSubBags', 'MarkTwoSubBags',
570
** and 'MarkAllSubBags' are really too simple for an explanation.
571
**
572
** 'MarkAllSubBagsDefault' is the same as 'MarkAllSubBags' but is only used
573
** by GASMAN as default. This will allow to catch type clashes.
574
*/
575
TNumMarkFuncBags TabMarkFuncBags [ NTYPES ];
576
#ifdef BOEHM_GC
577
int TabMarkTypeBags [ NTYPES ];
578
#endif
579
580
581
void InitMarkFuncBags (
582
UInt type,
583
TNumMarkFuncBags mark_func )
584
{
585
#ifdef BOEHM_GC
586
int mark_type;
587
#endif
588
#ifdef CHECK_FOR_CLASH_IN_INIT_MARK_FUNC
589
char str[256];
590
591
if ( TabMarkFuncBags[type] != MarkAllSubBagsDefault ) {
592
str[0] = 0;
593
strncat( str, "warning: mark function for type ", 32 );
594
str[32] = '0' + ((type/100) % 10);
595
str[33] = '0' + ((type/ 10) % 10);
596
str[34] = '0' + ((type/ 1) % 10);
597
str[35] = 0;
598
strncat( str, " already installed\n", 19 );
599
SyFputs( str, 0 );
600
}
601
#endif
602
TabMarkFuncBags[type] = mark_func;
603
#ifdef BOEHM_GC
604
if (mark_func == MarkNoSubBags)
605
mark_type = 0;
606
else if (mark_func == MarkAllSubBags)
607
mark_type = -1;
608
else if (mark_func == MarkOneSubBags)
609
mark_type = 1;
610
else if (mark_func == MarkTwoSubBags)
611
mark_type = 2;
612
else if (mark_func == MarkThreeSubBags)
613
mark_type = 3;
614
else if (mark_func == MarkFourSubBags)
615
mark_type = 4;
616
else
617
mark_type = -1;
618
TabMarkTypeBags[type] = mark_type;
619
#endif
620
}
621
622
623
void MarkNoSubBags (
624
Bag bag )
625
{
626
}
627
628
void MarkOneSubBags (
629
Bag bag )
630
{
631
Bag sub; /* one subbag identifier */
632
sub = PTR_BAG(bag)[0];
633
MARK_BAG( sub );
634
}
635
636
void MarkTwoSubBags (
637
Bag bag )
638
{
639
Bag sub; /* one subbag identifier */
640
sub = PTR_BAG(bag)[0];
641
MARK_BAG( sub );
642
sub = PTR_BAG(bag)[1];
643
MARK_BAG( sub );
644
}
645
646
void MarkThreeSubBags (
647
Bag bag )
648
{
649
Bag sub; /* one subbag identifier */
650
sub = PTR_BAG(bag)[0];
651
MARK_BAG( sub );
652
sub = PTR_BAG(bag)[1];
653
MARK_BAG( sub );
654
sub = PTR_BAG(bag)[2];
655
MARK_BAG( sub );
656
}
657
658
void MarkFourSubBags (
659
Bag bag )
660
{
661
Bag sub; /* one subbag identifier */
662
sub = PTR_BAG(bag)[0];
663
MARK_BAG( sub );
664
sub = PTR_BAG(bag)[1];
665
MARK_BAG( sub );
666
sub = PTR_BAG(bag)[2];
667
MARK_BAG( sub );
668
sub = PTR_BAG(bag)[3];
669
MARK_BAG( sub );
670
}
671
672
void MarkAllSubBags (
673
Bag bag )
674
{
675
Bag * ptr; /* pointer into the bag */
676
Bag sub; /* one subbag identifier */
677
UInt i; /* loop variable */
678
679
/* mark everything */
680
ptr = PTR_BAG( bag );
681
for ( i = SIZE_BAG(bag)/sizeof(Bag); 0 < i; i-- ) {
682
sub = ptr[i-1];
683
MARK_BAG( sub );
684
}
685
686
}
687
688
void MarkAllSubBagsDefault (
689
Bag bag )
690
{
691
Bag * ptr; /* pointer into the bag */
692
Bag sub; /* one subbag identifier */
693
UInt i; /* loop variable */
694
695
/* mark everything */
696
ptr = PTR_BAG( bag );
697
for ( i = SIZE_BAG(bag)/sizeof(Bag); 0 < i; i-- ) {
698
sub = ptr[i-1];
699
MARK_BAG( sub );
700
}
701
702
}
703
704
705
void MarkBagWeakly(
706
Bag bag )
707
{
708
if ( (((UInt)bag) & (sizeof(Bag)-1)) == 0 /* really looks like a pointer */
709
&& (Bag)MptrBags <= bag /* in plausible range */
710
&& bag < (Bag)OldBags /* " " " */
711
&& YoungBags < PTR_BAG(bag) /* points to a young bag */
712
&& PTR_BAG(bag) <= AllocBags /* " " " " " */
713
&& IS_MARKED_DEAD(bag) ) /* and not marked already */
714
{
715
LINK_BAG(bag) = (Bag)MARKED_HALFDEAD(bag); /* mark it now as we
716
don't have to recurse */
717
}
718
}
719
720
721
#ifdef BOEHM_GC
722
static GC_descr GCDesc[MAX_GC_PREFIX_DESC+1];
723
static unsigned GCKind[MAX_GC_PREFIX_DESC+1];
724
static GC_descr GCMDesc[MAX_GC_PREFIX_DESC+1];
725
static unsigned GCMKind[MAX_GC_PREFIX_DESC+1];
726
#endif
727
728
729
/****************************************************************************
730
**
731
*F CallbackForAllBags( <func> ) call a C function on all non-zero mptrs
732
**
733
** This calls a C function on every bag, including garbage ones, by simply
734
** walking the masterpointer area. Not terribly safe.
735
**
736
*/
737
738
void CallbackForAllBags(
739
void (*func)(Bag) )
740
{
741
#ifndef BOEHM_GC
742
Bag ptr;
743
for (ptr = (Bag)MptrBags; ptr < (Bag)OldBags; ptr ++)
744
if (*ptr != 0 && !IS_WEAK_DEAD_BAG(ptr) && (Bag)(*ptr) >= (Bag)OldBags)
745
{
746
(*func)(ptr);
747
}
748
#endif
749
}
750
751
752
/****************************************************************************
753
**
754
*V GlobalBags . . . . . . . . . . . . . . . . . . . . . list of global bags
755
*/
756
TNumGlobalBags GlobalBags;
757
758
759
/****************************************************************************
760
**
761
*F InitGlobalBag(<addr>, <cookie>) inform Gasman about global bag identifier
762
**
763
** 'InitGlobalBag' simply leaves the address <addr> in a global array, where
764
** it is used by 'CollectBags'. <cookie> is also recorded to allow things to
765
** be matched up after loading a saved workspace.
766
*/
767
static UInt GlobalSortingStatus;
768
Int WarnInitGlobalBag;
769
770
#ifndef BOEHM_GC
771
extern TNumAbortFuncBags AbortFuncBags;
772
773
void ClearGlobalBags ( void )
774
{
775
UInt i;
776
for (i = 0; i < GlobalBags.nr; i++)
777
{
778
GlobalBags.addr[i] = 0L;
779
GlobalBags.cookie[i] = 0L;
780
}
781
GlobalBags.nr = 0;
782
GlobalSortingStatus = 0;
783
WarnInitGlobalBag = 0;
784
return;
785
}
786
#endif
787
788
void InitGlobalBag (
789
Bag * addr,
790
const Char * cookie )
791
{
792
#ifndef BOEHM_GC
793
if ( GlobalBags.nr == NR_GLOBAL_BAGS ) {
794
(*AbortFuncBags)(
795
"Panic: Gasman cannot handle so many global variables" );
796
}
797
#ifdef DEBUG_GLOBAL_BAGS
798
{
799
UInt i;
800
if (cookie != (Char *)0)
801
for (i = 0; i < GlobalBags.nr; i++)
802
if ( 0 == strcmp(GlobalBags.cookie[i], cookie) )
803
if (GlobalBags.addr[i] == addr)
804
Pr("Duplicate global bag entry %s\n", (Int)cookie, 0L);
805
else
806
Pr("Duplicate global bag cookie %s\n", (Int)cookie, 0L);
807
}
808
#endif
809
if ( WarnInitGlobalBag ) {
810
Pr( "#W global bag '%s' initialized\n", (Int)cookie, 0L );
811
}
812
GlobalBags.addr[GlobalBags.nr] = addr;
813
GlobalBags.cookie[GlobalBags.nr] = cookie;
814
GlobalBags.nr++;
815
GlobalSortingStatus = 0;
816
#endif
817
}
818
819
820
821
#ifndef BOEHM_GC
822
static Int IsLessGlobal (
823
const Char * cookie1,
824
const Char * cookie2,
825
UInt byWhat )
826
{
827
if (byWhat != 2)
828
{
829
(*AbortFuncBags)("can only sort globals by cookie");
830
}
831
if (cookie1 == 0L && cookie2 == 0L)
832
return 0;
833
if (cookie1 == 0L)
834
return -1;
835
if (cookie2 == 0L)
836
return 1;
837
return strcmp(cookie1, cookie2) < 0;
838
}
839
#endif
840
841
842
843
void SortGlobals( UInt byWhat )
844
{
845
#ifndef BOEHM_GC
846
const Char *tmpcookie;
847
Bag * tmpaddr;
848
UInt len, h, i, k;
849
if (byWhat != 2)
850
{
851
(*AbortFuncBags)("can only sort globals by cookie");
852
}
853
if (GlobalSortingStatus == byWhat)
854
return;
855
len = GlobalBags.nr;
856
h = 1;
857
while ( 9*h + 4 < len )
858
{ h = 3*h + 1; }
859
while ( 0 < h ) {
860
for ( i = h; i < len; i++ ) {
861
tmpcookie = GlobalBags.cookie[i];
862
tmpaddr = GlobalBags.addr[i];
863
k = i;
864
while ( h <= k && IsLessGlobal(tmpcookie,
865
GlobalBags.cookie[k-h],
866
byWhat))
867
{
868
GlobalBags.cookie[k] = GlobalBags.cookie[k-h];
869
GlobalBags.addr[k] = GlobalBags.addr[k-h];
870
k -= h;
871
}
872
GlobalBags.cookie[k] = tmpcookie;
873
GlobalBags.addr[k] = tmpaddr;
874
}
875
h = h / 3;
876
}
877
GlobalSortingStatus = byWhat;
878
return;
879
#endif
880
}
881
882
883
884
Bag * GlobalByCookie(
885
const Char * cookie )
886
{
887
#ifndef BOEHM_GC
888
UInt i,top,bottom,middle;
889
Int res;
890
if (cookie == 0L)
891
{
892
Pr("Panic -- 0L cookie passed to GlobalByCookie\n",0L,0L);
893
SyExit(2);
894
}
895
if (GlobalSortingStatus != 2)
896
{
897
for (i = 0; i < GlobalBags.nr; i++)
898
{
899
if (strcmp(cookie, GlobalBags.cookie[i]) == 0)
900
return GlobalBags.addr[i];
901
}
902
return (Bag *)0L;
903
}
904
else
905
{
906
top = GlobalBags.nr;
907
bottom = 0;
908
while (top >= bottom) {
909
middle = (top + bottom)/2;
910
res = strcmp(cookie,GlobalBags.cookie[middle]);
911
if (res < 0)
912
top = middle-1;
913
else if (res > 0)
914
bottom = middle+1;
915
else
916
return GlobalBags.addr[middle];
917
}
918
return (Bag *)0L;
919
}
920
#else
921
return (Bag *) 0;
922
#endif /* !BOEHM_GC */
923
}
924
925
926
static Bag NextMptrRestoring;
927
extern TNumAllocFuncBags AllocFuncBags;
928
929
void StartRestoringBags( UInt nBags, UInt maxSize)
930
{
931
#ifndef BOEHM_GC
932
UInt target;
933
Bag *newmem;
934
/*Bag *ptr; */
935
target = (8*nBags)/7 + (8*maxSize)/7; /* ideal workspace size */
936
target = (target * sizeof (Bag) + (512L*1024L) - 1)/(512L*1024L)*(512L*1024L)/sizeof (Bag);
937
/* make sure that the allocated amount of memory is divisible by 512 * 1024 */
938
if (SizeWorkspace < target)
939
{
940
newmem = (*AllocFuncBags)(sizeof(Bag)*(target- SizeWorkspace)/1024, 0);
941
if (newmem == 0)
942
{
943
target = nBags + maxSize; /* absolute requirement */
944
target = (target * sizeof (Bag) + (512L*1024L) - 1)/(512L*1024L)*(512L*1024L)/sizeof (Bag);
945
/* make sure that the allocated amount of memory is divisible by 512 * 1024 */
946
if (SizeWorkspace < target)
947
(*AllocFuncBags)(sizeof(Bag)*(target- SizeWorkspace)/1024, 1);
948
}
949
EndBags = MptrBags + target;
950
}
951
OldBags = MptrBags + nBags + (SizeWorkspace - nBags - maxSize)/8;
952
AllocBags = OldBags;
953
NextMptrRestoring = (Bag)MptrBags;
954
SizeAllBags = 0;
955
NrAllBags = 0;
956
return;
957
#endif
958
}
959
960
Bag NextBagRestoring( UInt size, UInt type)
961
{
962
#ifndef BOEHM_GC
963
Bag bag;
964
UInt i;
965
*(Bag **)NextMptrRestoring = (AllocBags+HEADER_SIZE);
966
bag = NextMptrRestoring;
967
#ifdef USE_NEWSHAPE
968
((UInt *)AllocBags)[0] = (size << 16 | type);
969
#else
970
((UInt *)AllocBags)[0] = type;
971
((UInt *)AllocBags)[1] = size;
972
#endif
973
974
((Bag *)AllocBags)[HEADER_SIZE-1] = NextMptrRestoring;
975
NextMptrRestoring++;
976
#ifdef DEBUG_LOADING
977
if ((Bag *)NextMptrRestoring >= OldBags)
978
(*AbortFuncBags)("Overran Masterpointer area");
979
#endif
980
AllocBags += HEADER_SIZE;
981
982
for (i = 0; i < WORDS_BAG(size); i++)
983
*AllocBags++ = (Bag)0;
984
985
#ifdef DEBUG_LOADING
986
if (AllocBags > EndBags)
987
(*AbortFuncBags)("Overran data area");
988
#endif
989
#ifdef COUNT_BAGS
990
InfoBags[type].nrLive += 1;
991
InfoBags[type].nrAll += 1;
992
InfoBags[type].sizeLive += size;
993
InfoBags[type].sizeAll += size;
994
#endif
995
SizeAllBags += size;
996
NrAllBags ++;
997
return bag;
998
#else
999
return 0;
1000
#endif
1001
}
1002
1003
void FinishedRestoringBags( void )
1004
{
1005
#ifndef BOEHM_GC
1006
Bag p;
1007
/* Bag *ptr; */
1008
YoungBags = AllocBags;
1009
StopBags = AllocBags + WORDS_BAG(AllocSizeBags);
1010
if (StopBags > EndBags)
1011
StopBags = EndBags;
1012
FreeMptrBags = NextMptrRestoring;
1013
for (p = NextMptrRestoring; p +1 < (Bag)OldBags; p++)
1014
*(Bag *)p = p+1;
1015
*p = 0;
1016
NrLiveBags = NrAllBags;
1017
SizeLiveBags = SizeAllBags;
1018
NrDeadBags = 0;
1019
SizeDeadBags = 0;
1020
NrHalfDeadBags = 0;
1021
ChangedBags = 0;
1022
return;
1023
#endif
1024
}
1025
1026
1027
#ifndef BOEHM_GC
1028
/****************************************************************************
1029
**
1030
*F InitFreeFuncBag(<type>,<free-func>) . . . . . . install freeing function
1031
**
1032
** 'InitFreeFuncBag' is really too simple for an explanation.
1033
*/
1034
TNumFreeFuncBags TabFreeFuncBags [ 256 ];
1035
1036
UInt NrTabFreeFuncBags;
1037
1038
void InitFreeFuncBag (
1039
UInt type,
1040
TNumFreeFuncBags free_func )
1041
{
1042
if ( free_func != 0 ) {
1043
NrTabFreeFuncBags = NrTabFreeFuncBags + 1;
1044
}
1045
else {
1046
NrTabFreeFuncBags = NrTabFreeFuncBags - 1;
1047
}
1048
TabFreeFuncBags[type] = free_func;
1049
}
1050
#endif
1051
1052
1053
/****************************************************************************
1054
**
1055
*F InitCollectFuncBags(<bfr-func>,<aft-func>) . install collection functions
1056
**
1057
** 'InitCollectFuncBags' is really too simple for an explanation.
1058
*/
1059
TNumCollectFuncBags BeforeCollectFuncBags;
1060
1061
TNumCollectFuncBags AfterCollectFuncBags;
1062
1063
void InitCollectFuncBags (
1064
TNumCollectFuncBags before_func,
1065
TNumCollectFuncBags after_func )
1066
{
1067
#ifndef BOEHM_GC
1068
BeforeCollectFuncBags = before_func;
1069
AfterCollectFuncBags = after_func;
1070
#endif
1071
}
1072
1073
1074
/****************************************************************************
1075
**
1076
*F FinishBags() . . . . . . . . . . . . . . . . . . . . . . .finalize GASMAN
1077
**
1078
** `FinishBags()' ends GASMAN and returns all memory to the OS pool
1079
**
1080
*/
1081
1082
void FinishBags( void )
1083
{
1084
#ifndef BOEHM_GC
1085
(*AllocFuncBags)(-(sizeof(Bag)*SizeWorkspace/1024),2);
1086
return;
1087
#endif
1088
}
1089
1090
/****************************************************************************
1091
**
1092
*F InitBags(...) . . . . . . . . . . . . . . . . . . . . . initialize Gasman
1093
**
1094
** 'InitBags' remembers <alloc-func>, <stack-func>, <stack-bottom>,
1095
** <stack-align>, <cache-size>, <dirty>, and <abort-func> in global
1096
** variables. It also allocates the initial workspace, and sets up the
1097
** linked list of available masterpointer.
1098
*/
1099
TNumAllocFuncBags AllocFuncBags;
1100
1101
TNumStackFuncBags StackFuncBags;
1102
1103
Bag * StackBottomBags;
1104
1105
UInt StackAlignBags;
1106
1107
UInt CacheSizeBags;
1108
1109
UInt DirtyBags;
1110
1111
TNumAbortFuncBags AbortFuncBags;
1112
1113
#ifdef BOEHM_GC
1114
1115
/*
1116
* Build memory layout information for Boehm GC.
1117
*
1118
* Bitmapped type descriptors have a bit set if the word at the
1119
* corresponding offset may contain a reference. This is done
1120
* by first creating a bitmap and then using GC_make_descriptor()
1121
* to build a descriptor from the bitmap. Memory for a specific
1122
* type layout can be allocated with GC_malloc_explicitly_typed()
1123
* and GC_malloc_explicitly_typed_ignore_off_page().
1124
*
1125
* We also create a new 'kind' for each collector. Kinds have their
1126
* own associated free lists and do not require to have type information
1127
* stored in each bag, thus potentially saving some memory. Allocating
1128
* memory of a specific kind is done with GC_generic_malloc(). There
1129
* is no public _ignore_off_page() version for this call, so we use
1130
* GC_malloc_explicitly_typed_ignore_off_page() instead, given that
1131
* the overhead is negligible for large objects.
1132
*/
1133
1134
void BuildPrefixGCDescriptor(unsigned prefix_len) {
1135
1136
if (prefix_len) {
1137
GC_word bits[1] = {0};
1138
unsigned i;
1139
for (i=0; i<prefix_len; i++)
1140
GC_set_bit(bits, (i + HEADER_SIZE));
1141
GCDesc[prefix_len] = GC_make_descriptor(bits, prefix_len + HEADER_SIZE);
1142
GC_set_bit(bits, 0);
1143
GCMDesc[prefix_len] = GC_make_descriptor(bits, prefix_len + HEADER_SIZE);
1144
} else {
1145
GCDesc[prefix_len] = GC_DS_LENGTH;
1146
GCMDesc[prefix_len] = GC_DS_LENGTH | sizeof(void *);
1147
}
1148
GCKind[prefix_len] = GC_new_kind(GC_new_free_list(), GCDesc[prefix_len],
1149
0, 1);
1150
GCMKind[prefix_len] = GC_new_kind(GC_new_free_list(), GCMDesc[prefix_len],
1151
0, 0);
1152
}
1153
1154
#endif
1155
1156
#ifdef BOEHM_GC
1157
static void TLAllocatorInit(void);
1158
#endif
1159
1160
void InitBags (
1161
TNumAllocFuncBags alloc_func,
1162
UInt initial_size,
1163
TNumStackFuncBags stack_func,
1164
Bag * stack_bottom,
1165
UInt stack_align,
1166
UInt cache_size,
1167
UInt dirty,
1168
TNumAbortFuncBags abort_func )
1169
{
1170
UInt i; /* loop variable */
1171
#ifndef BOEHM_GC
1172
Bag * p; /* loop variable */
1173
1174
ClearGlobalBags();
1175
WarnInitGlobalBag = 0;
1176
1177
/* install the allocator and the abort function */
1178
AllocFuncBags = alloc_func;
1179
AbortFuncBags = abort_func;
1180
1181
/* install the stack marking function and values */
1182
StackFuncBags = stack_func;
1183
StackBottomBags = stack_bottom;
1184
StackAlignBags = stack_align;
1185
#if ITANIUM
1186
ItaniumSpecialMarkingInit();
1187
#endif
1188
1189
/* first get some storage from the operating system */
1190
initial_size = (initial_size + 511) & ~(511);
1191
MptrBags = (*AllocFuncBags)( initial_size, 1 );
1192
if ( MptrBags == 0 )
1193
(*AbortFuncBags)("cannot get storage for the initial workspace.");
1194
EndBags = MptrBags + 1024*(initial_size / sizeof(Bag*));
1195
1196
/* 1/8th of the storage goes into the masterpointer area */
1197
FreeMptrBags = (Bag)MptrBags;
1198
for ( p = MptrBags;
1199
p + 2*(SIZE_MPTR_BAGS) <= MptrBags+1024*initial_size/8/sizeof(Bag*);
1200
p += SIZE_MPTR_BAGS )
1201
{
1202
*p = (Bag)(p + SIZE_MPTR_BAGS);
1203
}
1204
1205
/* the rest is for bags */
1206
OldBags = MptrBags + 1024*initial_size/8/sizeof(Bag*);
1207
YoungBags = OldBags;
1208
AllocBags = OldBags;
1209
1210
/* remember the cache size */
1211
CacheSizeBags = cache_size;
1212
if ( ! CacheSizeBags ) {
1213
AllocSizeBags = 256;
1214
StopBags = EndBags;
1215
}
1216
else {
1217
AllocSizeBags = (CacheSizeBags+1023)/1024;
1218
StopBags = AllocBags + WORDS_BAG(1024*AllocSizeBags) <= EndBags ?
1219
AllocBags + WORDS_BAG(1024*AllocSizeBags) : EndBags;
1220
}
1221
1222
/* remember whether bags should be clean */
1223
DirtyBags = dirty;
1224
1225
/* install the marking functions */
1226
for ( i = 0; i < 255; i++ ) {
1227
TabMarkFuncBags[i] = MarkAllSubBagsDefault;
1228
}
1229
1230
/* Set ChangedBags to a proper initial value */
1231
ChangedBags = 0;
1232
#else /* BOEHM_GC */
1233
/* install the marking functions */
1234
for ( i = 0; i < 255; i++ ) {
1235
TabMarkFuncBags[i] = MarkAllSubBagsDefault;
1236
TabMarkTypeBags[i] = -1;
1237
}
1238
#ifndef DISABLE_GC
1239
if (!getenv("GC_MARKERS")) {
1240
/* The Boehm GC does not have an API to set the number of
1241
* markers for the parallel mark and sweep implementation,
1242
* so we use the documented environment variable GC_MARKERS
1243
* instead. However, we do not override it if it's already
1244
* set.
1245
*/
1246
static char marker_env_str[32];
1247
unsigned num_markers = 2;
1248
extern UInt SyNumProcessors;
1249
extern UInt SyNumGCThreads;
1250
if (!SyNumGCThreads)
1251
SyNumGCThreads = SyNumProcessors;
1252
if (SyNumGCThreads) {
1253
if (SyNumGCThreads <= MAX_GC_THREADS)
1254
num_markers = (unsigned) SyNumProcessors;
1255
else
1256
num_markers = MAX_GC_THREADS;
1257
}
1258
sprintf(marker_env_str, "GC_MARKERS=%u", num_markers);
1259
putenv(marker_env_str);
1260
}
1261
GC_set_all_interior_pointers(0);
1262
GC_init();
1263
TLAllocatorInit();
1264
GC_register_displacement(0);
1265
GC_register_displacement(HEADER_SIZE*sizeof(Bag));
1266
initial_size *= 1024;
1267
if (GC_get_heap_size() < initial_size)
1268
GC_expand_hp(initial_size - GC_get_heap_size());
1269
if (SyStorKill)
1270
GC_set_max_heap_size(SyStorKill * 1024);
1271
AddGCRoots();
1272
CreateMainRegion();
1273
for (i=0; i<=MAX_GC_PREFIX_DESC; i++) {
1274
BuildPrefixGCDescriptor(i);
1275
/* This is necessary to initialize some internal structures
1276
* in the garbage collector: */
1277
GC_generic_malloc((HEADER_SIZE + i) * sizeof(UInt), GCMKind[i]);
1278
}
1279
#endif /* DISABLE_GC */
1280
#endif /* BOEHM_GC */
1281
}
1282
1283
#ifdef BOEHM_GC
1284
1285
#define GRANULE_SIZE (2 * sizeof(UInt))
1286
1287
static unsigned char TLAllocatorSeg[TL_GC_SIZE / GRANULE_SIZE + 1];
1288
static unsigned TLAllocatorSize[TL_GC_SIZE / GRANULE_SIZE];
1289
static UInt TLAllocatorMaxSeg;
1290
1291
static void TLAllocatorInit(void) {
1292
unsigned stage = 16;
1293
unsigned inc = 1;
1294
unsigned i = 0;
1295
unsigned k = 0;
1296
unsigned j;
1297
unsigned max = TL_GC_SIZE / GRANULE_SIZE;
1298
while (i <= max) {
1299
if (i == stage) {
1300
stage *= 2;
1301
inc *= 2;
1302
}
1303
TLAllocatorSize[k] = i * GRANULE_SIZE;
1304
TLAllocatorSeg[i] = k;
1305
for (j=1; j<inc; j++) {
1306
if (i + j <= max)
1307
TLAllocatorSeg[i+j] = k+1;
1308
}
1309
i += inc;
1310
k ++;
1311
}
1312
TLAllocatorMaxSeg = k;
1313
if (MAX_GC_PREFIX_DESC * sizeof(void *) > sizeof(TLS(FreeList)))
1314
abort();
1315
}
1316
1317
/****************************************************************************
1318
**
1319
*F AllocateBagMemory( <gc_type>, <type>, <size> )
1320
**
1321
** Allocate memory for a new bag.
1322
**
1323
** 'AllocateBagMemory' is an auxiliary routine for the Boehm GC that
1324
** allocates memory from the appropriate pool. 'gc_type' is -1 if all words
1325
** in the bag can refer to other bags, 0 if the bag will not contain any
1326
** references to other bags, and > 0 to indicate a specific memory layout
1327
** descriptor.
1328
**/
1329
void *AllocateBagMemory(int gc_type, int type, UInt size)
1330
{
1331
void *result = NULL;
1332
if (size <= TL_GC_SIZE) {
1333
UInt alloc_seg, alloc_size;
1334
alloc_size = (size + GRANULE_SIZE - 1 ) / GRANULE_SIZE;
1335
alloc_seg = TLAllocatorSeg[alloc_size];
1336
alloc_size = TLAllocatorSize[alloc_seg];
1337
if (!TLS(FreeList)[gc_type+1])
1338
TLS(FreeList)[gc_type+1] =
1339
GC_malloc(sizeof(void *) * TLAllocatorMaxSeg);
1340
if (!(result = TLS(FreeList)[gc_type+1][alloc_seg])) {
1341
if (gc_type < 0)
1342
TLS(FreeList)[0][alloc_seg] = GC_malloc_many(alloc_size);
1343
else
1344
GC_generic_malloc_many(alloc_size, GCMKind[gc_type],
1345
&TLS(FreeList)[gc_type+1][alloc_seg]);
1346
result = TLS(FreeList)[gc_type+1][alloc_seg];
1347
}
1348
TLS(FreeList)[gc_type+1][alloc_seg] = *(void **)result;
1349
memset(result, 0, alloc_size);
1350
} else {
1351
if (gc_type >= 0)
1352
result = GC_generic_malloc(size, GCKind[gc_type]);
1353
else
1354
result = GC_malloc(size);
1355
}
1356
if (TabFinalizerFuncBags[type])
1357
GC_register_finalizer_no_order(result, StandardFinalizer,
1358
NULL, NULL, NULL);
1359
return result;
1360
}
1361
#endif
1362
1363
1364
/****************************************************************************
1365
**
1366
*F NewBag( <type>, <size> ) . . . . . . . . . . . . . . allocate a new bag
1367
**
1368
** 'NewBag' is actually quite simple.
1369
**
1370
** It first tests whether enough storage is available in the allocation area
1371
** and whether a free masterpointer is available. If not, it starts a
1372
** garbage collection by calling 'CollectBags' passing <size> as the size of
1373
** the bag it is currently allocating and 0 to indicate that only a partial
1374
** garbage collection is called for. If 'CollectBags' fails and returns 0,
1375
** 'NewBag' also fails and also returns 0.
1376
**
1377
** Then it takes the first free masterpointer from the linked list of free
1378
** masterpointers (see "FreeMptrBags").
1379
**
1380
** Then it writes the size and the type into the word pointed to by
1381
** 'AllocBags'. Then it writes the identifier, i.e., the location of the
1382
** masterpointer, into the next word.
1383
**
1384
** Then it advances 'AllocBags' by '2 + WORDS_BAG(<size>)'.
1385
**
1386
** Finally it returns the identifier of the new bag.
1387
**
1388
** Note that 'NewBag' never initializes the new bag to contain only 0. If
1389
** this is desired because the initialization flag <dirty> (see "InitBags")
1390
** was 0, it is the job of 'CollectBags' to initialize the new free space
1391
** after a garbage collection.
1392
**
1393
** If {\Gasman} was compiled with the option 'COUNT_BAGS' then 'NewBag' also
1394
** updates the information in 'InfoBags' (see "InfoBags").
1395
**
1396
** 'NewBag' is implemented as a function instead of a macro for three
1397
** reasons. It reduces the size of the program, improving the instruction
1398
** cache hit ratio. The compiler can do anti-aliasing analysis for the
1399
** local variables of the function. To enable statistics only {\Gasman}
1400
** needs to be recompiled.
1401
*/
1402
Bag NewBag (
1403
UInt type,
1404
UInt size )
1405
{
1406
Bag bag; /* identifier of the new bag */
1407
Bag * dst; /* destination of the new bag */
1408
#ifdef BOEHM_GC
1409
UInt alloc_size;
1410
#endif
1411
1412
#ifndef BOEHM_GC
1413
#ifdef TREMBLE_HEAP
1414
CollectBags(0,0);
1415
#endif
1416
1417
/* check that a masterpointer and enough storage are available */
1418
if ( (FreeMptrBags == 0 || SizeAllocationArea < HEADER_SIZE+WORDS_BAG(size))
1419
&& CollectBags( size, 0 ) == 0 )
1420
{
1421
return 0;
1422
}
1423
1424
#ifdef COUNT_BAGS
1425
/* update the statistics */
1426
NrAllBags += 1;
1427
InfoBags[type].nrLive += 1;
1428
InfoBags[type].nrAll += 1;
1429
InfoBags[type].sizeLive += size;
1430
InfoBags[type].sizeAll += size;
1431
#endif
1432
SizeAllBags += size;
1433
1434
/* get the identifier of the bag and set 'FreeMptrBags' to the next */
1435
bag = FreeMptrBags;
1436
FreeMptrBags = *(Bag*)bag;
1437
CLEAR_CANARY();
1438
/* allocate the storage for the bag */
1439
dst = AllocBags;
1440
AllocBags = dst + HEADER_SIZE + WORDS_BAG(size);
1441
ADD_CANARY();
1442
#else /* BOEHM_GC */
1443
alloc_size = HEADER_SIZE*sizeof(Bag) + size;
1444
#ifndef DISABLE_GC
1445
#ifndef TRACK_CREATOR
1446
bag = GC_malloc(2*sizeof(Bag *));
1447
#else
1448
bag = GC_malloc(4*sizeof(Bag *));
1449
if (TLS(PtrLVars)) {
1450
bag[2] = (void *)(CURR_FUNC);
1451
if (TLS(CurrLVars) != TLS(BottomLVars)) {
1452
Obj plvars = ADDR_OBJ(TLS(CurrLVars))[2];
1453
bag[3] = (void *) (ADDR_OBJ(plvars)[0]);
1454
}
1455
}
1456
#endif
1457
/* If the size of an object is zero (such as an empty permutation),
1458
* and the header size is a multiple of twice the word size of the
1459
* architecture, then the master pointer will actually point past
1460
* the allocated area. Because this would result in the object
1461
* being freed prematurely, we will allocate at least one extra
1462
* byte so that the master pointer actually points to within an
1463
* allocated memory area.
1464
*/
1465
if (size == 0)
1466
alloc_size++;
1467
/* While we use the Boehm GC without the "all interior pointers"
1468
* option, stack references to the interior of an object will
1469
* still be valid from any reference on the stack. This can lead,
1470
* for example, to a 1GB string never being freed if there's an
1471
* integer on the stack that happens to also be a reference to
1472
* any character inside that string. The garbage collector does
1473
* this because after compiler optimizations (especially reduction
1474
* in strength) references to the beginning of an object may be
1475
* lost.
1476
*
1477
* However, this is not generally a risk with GAP objects, because
1478
* master pointers on the heap will always retain a reference to
1479
* the start of the object (or, more precisely, to the first byte
1480
* past the header area). Hence, compiler optimizations pose no
1481
* actual risk unless the master pointer is destroyed also.
1482
*
1483
* To avoid the scenario where large objects do not get deallocated,
1484
* we therefore use the _ignore_off_page() calls. One caveat here
1485
* is that these calls do not use thread-local allocation, making
1486
* them somewhat slower. Hence, we only use them for sufficiently
1487
* large objects.
1488
*/
1489
dst = AllocateBagMemory(TabMarkTypeBags[type], type, alloc_size);
1490
#else
1491
bag = malloc(2*sizeof(Bag *));
1492
dst = malloc(alloc_size);
1493
memset(dst, 0, alloc_size);
1494
#endif /* DISABLE_GC */
1495
#endif /* BOEHM_GC */
1496
1497
/* enter size-type words */
1498
#ifdef USE_NEWSHAPE
1499
*dst++ = (Bag)(size << 16 | type);
1500
#else
1501
*dst++ = (Bag)(type);
1502
*dst++ = (Bag)(size);
1503
#endif
1504
1505
1506
/* enter link word */
1507
*dst++ = bag;
1508
1509
/* set the masterpointer */
1510
PTR_BAG(bag) = dst;
1511
#if 0
1512
{
1513
extern void * stderr;
1514
UInt i;
1515
for (i = 0; i < WORDS_BAG(size); i++)
1516
if (*dst++)
1517
fprintf(stderr, "dirty bag being returned\n");
1518
}
1519
#endif
1520
/* return the identifier of the new bag */
1521
return bag;
1522
}
1523
1524
1525
/****************************************************************************
1526
**
1527
*F RetypeBag(<bag>,<new>) . . . . . . . . . . . . change the type of a bag
1528
**
1529
** 'RetypeBag' is very simple.
1530
**
1531
** All it has to do is to change the size-type word of the bag.
1532
**
1533
** If {\Gasman} was compiled with the option 'COUNT_BAGS' then 'RetypeBag'
1534
** also updates the information in 'InfoBags' (see "InfoBags").
1535
*/
1536
void RetypeBag (
1537
Bag bag,
1538
UInt new_type )
1539
{
1540
1541
#ifdef COUNT_BAGS
1542
/* update the statistics */
1543
{
1544
UInt old_type; /* old type of the bag */
1545
UInt size;
1546
1547
old_type = TNUM_BAG(bag);
1548
size = SIZE_BAG(bag);
1549
InfoBags[old_type].nrLive -= 1;
1550
InfoBags[new_type].nrLive += 1;
1551
InfoBags[old_type].nrAll -= 1;
1552
InfoBags[new_type].nrAll += 1;
1553
InfoBags[old_type].sizeLive -= size;
1554
InfoBags[new_type].sizeLive += size;
1555
InfoBags[old_type].sizeAll -= size;
1556
InfoBags[new_type].sizeAll += size;
1557
}
1558
#else
1559
#ifdef BOEHM_GC
1560
UInt old_type = TNUM_BAG(bag);
1561
#endif
1562
#endif
1563
1564
/* change the size-type word */
1565
#ifdef USE_NEWSHAPE
1566
*(*bag-HEADER_SIZE) &= 0xFFFFFFFFFFFF0000L;
1567
*(*bag-HEADER_SIZE) |= new_type;
1568
#else
1569
*(*bag-HEADER_SIZE) = new_type;
1570
#endif
1571
#ifdef BOEHM_GC
1572
{
1573
int old_gctype, new_gctype;
1574
UInt size;
1575
void *new_mem, *old_mem;
1576
old_gctype = TabMarkTypeBags[old_type];
1577
new_gctype = TabMarkTypeBags[new_type];
1578
if (old_gctype != new_gctype) {
1579
size = SIZE_BAG(bag) + HEADER_SIZE * sizeof(Bag);
1580
new_mem = AllocateBagMemory(new_gctype, new_type, size);
1581
old_mem = PTR_BAG(bag);
1582
old_mem = ((char *) old_mem) - HEADER_SIZE * sizeof(Bag);
1583
memcpy(new_mem, old_mem, size);
1584
PTR_BAG(bag) = (void *)(((char *)new_mem) + HEADER_SIZE * sizeof(Bag));
1585
}
1586
}
1587
#endif
1588
}
1589
1590
1591
/****************************************************************************
1592
**
1593
*F ResizeBag(<bag>,<new>) . . . . . . . . . . . . change the size of a bag
1594
**
1595
** Basically 'ResizeBag' is rather simple, but there are a few traps that
1596
** must be avoided.
1597
**
1598
** If the size of the bag changes only a little bit, so that the number of
1599
** words needed for the data area does not change, 'ResizeBag' only changes
1600
** the size-type word of the bag.
1601
**
1602
** If the bag is to be shrunk and at least one word becomes free,
1603
** 'ResizeBag' changes the size-type word of the bag, and stores a magic
1604
** size-type word in the first free word. This magic size-type word has
1605
** type 255 and the size is the number of following free bytes, which is
1606
** always divisible by 'sizeof(Bag)'. The type 255 allows 'CollectBags' to
1607
** detect that this body is the remainder of a resize operation, and the
1608
** size allows it to know how many bytes there are in this body (see
1609
** "Implementation of CollectBags").
1610
**
1611
** So for example if 'ResizeBag' shrinks a bag of type 7 from 18 bytes to 10
1612
** bytes the situation before 'ResizeBag' is as follows{\:}
1613
**
1614
** +---------+
1615
** |<masterp>|
1616
** +---------+
1617
** \_____________
1618
** \
1619
** V
1620
** +---------+---------+--------------------------------------------+----+
1621
** | 18 . 7 | <link> | . . . . | pad|
1622
** +---------+---------+--------------------------------------------+----+
1623
**
1624
** And after 'ResizeBag' the situation is as follows{\:}
1625
**
1626
** +---------+
1627
** |<masterp>|
1628
** +---------+
1629
** \_____________
1630
** \
1631
** V
1632
** +---------+---------+------------------------+----+---------+---------+
1633
** | 10 . 7 | <link> | . . | pad| 4 .255| |
1634
** +---------+---------+------------------------+----+---------+---------+
1635
**
1636
** If the bag is to be extended and it is that last allocated bag, so that
1637
** it is immediately adjacent to the allocation area, 'ResizeBag' simply
1638
** increments 'AllocBags' after making sure that enough space is available
1639
** in the allocation area (see "Layout of the Workspace").
1640
**
1641
** If the bag is to be extended and it is not the last allocated bag,
1642
** 'ResizeBag' first allocates a new bag similar to 'NewBag', but without
1643
** using a new masterpointer. Then it copies the old contents to the new
1644
** bag. Finally it resets the masterpointer of the bag to point to the new
1645
** address. Then it changes the type of the old body to 255, so that the
1646
** garbage collection can detect that this body is the remainder of a resize
1647
** (see "Implementation of NewBag" and "Implementation of CollectBags").
1648
**
1649
** When an old bag is extended, it will now reside in the young bags area,
1650
** and thus appear to be young. Since old bags are supposed to survive
1651
** partial garbage collections 'ResizeBag' must somehow protect this bag
1652
** from partial garbage collections. This is done by putting this bag onto
1653
** the linked list of changed bags (see "ChangedBags"). When a partial
1654
** garbage collection sees a young bag on the list of changed bags, it knows
1655
** that it is the result of 'ResizeBag' of an old bag, and does not throw it
1656
** away (see "Implementation of CollectBags"). Note that when 'ResizeBag'
1657
** tries this, the bag may already be on the linked list, either because it
1658
** has been resized earlier, or because it has been changed. In this case
1659
** 'ResizeBag' simply keeps the bag on this linked list.
1660
**
1661
** If {\Gasman} was compiled with the option 'COUNT_BAGS' then 'ResizeBag'
1662
** also updates the information in 'InfoBags' (see "InfoBags").
1663
*/
1664
1665
UInt ResizeBag (
1666
Bag bag,
1667
UInt new_size )
1668
{
1669
UInt type; /* type of the bag */
1670
UInt old_size; /* old size of the bag */
1671
Bag * dst; /* destination in copying */
1672
Bag * src; /* source in copying */
1673
#ifndef BOEHM_GC
1674
Bag * end; /* end in copying */
1675
#else
1676
UInt alloc_size;
1677
#endif
1678
1679
/* check the size */
1680
1681
#ifdef TREMBLE_HEAP
1682
CollectBags(0,0);
1683
#endif
1684
1685
/* get type and old size of the bag */
1686
type = TNUM_BAG(bag);
1687
old_size = SIZE_BAG(bag);
1688
1689
#ifdef COUNT_BAGS
1690
/* update the statistics */
1691
InfoBags[type].sizeLive += new_size - old_size;
1692
InfoBags[type].sizeAll += new_size - old_size;
1693
#endif
1694
SizeAllBags += new_size - old_size;
1695
1696
/* if the real size of the bag doesn't change */
1697
#ifndef BOEHM_GC
1698
if ( WORDS_BAG(new_size) == WORDS_BAG(old_size) ) {
1699
#else
1700
#ifndef DISABLE_GC
1701
alloc_size = GC_size(PTR_BAG(bag)-HEADER_SIZE);
1702
/* An alternative implementation would be to compare
1703
* new_size <= alloc_size in the following test in order
1704
* to avoid reallocations for alternating contractions
1705
* and expansions. However, typed allocation in the Boehm
1706
* GC stores layout information in the last word of a memory
1707
* block and we may accidentally overwrite this information,
1708
* because GC_size() includes that extraneous word when
1709
* returning the size of a memory block.
1710
*
1711
* This is technically a bug in GC_size(), but until and
1712
* unless there is an upstream fix, we'll do it the safe
1713
* way.
1714
*/
1715
if ( new_size <= old_size
1716
&& HEADER_SIZE*sizeof(Bag) + new_size >= alloc_size * 3/4) {
1717
#else
1718
if (new_size <= old_size) {
1719
#endif /* DISABLE_GC */
1720
#endif
1721
1722
/* change the size word */
1723
#ifdef USE_NEWSHAPE
1724
*(*bag-2) = (new_size << 16 | type);
1725
#else
1726
*(*bag-2) = new_size;
1727
#endif
1728
}
1729
1730
/* if the bag is shrunk */
1731
/* we must not shrink the last bag by moving 'AllocBags', */
1732
/* since the remainder may not be zero filled */
1733
#ifndef BOEHM_GC
1734
else if ( WORDS_BAG(new_size) < WORDS_BAG(old_size) ) {
1735
1736
/* leave magic size-type word for the sweeper, type must be 255 */
1737
if ((WORDS_BAG(old_size)-WORDS_BAG(new_size) == 1))
1738
*(UInt*)(PTR_BAG(bag) + WORDS_BAG(new_size)) = 1 << 8 | 255;
1739
else
1740
{
1741
#ifdef USE_NEWSHAPE
1742
*(UInt*)(PTR_BAG(bag) + WORDS_BAG(new_size)) =
1743
(WORDS_BAG(old_size)-WORDS_BAG(new_size)-1)*sizeof(Bag) << 16 | 255;
1744
#else
1745
*(UInt*)(PTR_BAG(bag) + WORDS_BAG(new_size)) = 255;
1746
*(UInt*)(PTR_BAG(bag) + WORDS_BAG(new_size) + 1) =
1747
(WORDS_BAG(old_size)-WORDS_BAG(new_size)-1)*sizeof(Bag);
1748
#endif
1749
}
1750
1751
/* change the size- word */
1752
#ifdef USE_NEWSHAPE
1753
*(*bag-2) = (new_size << 16 | type);
1754
#else
1755
*(*bag-2) = new_size;
1756
#endif
1757
1758
1759
}
1760
1761
/* if the last bag is to be enlarged */
1762
else if ( PTR_BAG(bag) + WORDS_BAG(old_size) == AllocBags ) {
1763
CLEAR_CANARY();
1764
/* check that enough storage for the new bag is available */
1765
if ( StopBags < PTR_BAG(bag)+WORDS_BAG(new_size)
1766
&& CollectBags( new_size-old_size, 0 ) == 0 ) {
1767
return 0;
1768
}
1769
1770
/* simply increase the free pointer */
1771
if ( YoungBags == AllocBags )
1772
YoungBags += WORDS_BAG(new_size) - WORDS_BAG(old_size);
1773
AllocBags += WORDS_BAG(new_size) - WORDS_BAG(old_size);
1774
1775
ADD_CANARY();
1776
/* change the size-type word */
1777
#ifdef USE_NEWSHAPE
1778
*(*bag-2) = (new_size << 16 | type);
1779
#else
1780
*(*bag-2) = new_size;
1781
#endif
1782
}
1783
#endif /* !BOEHM_GC */
1784
/* if the bag is enlarged */
1785
else {
1786
1787
#ifndef BOEHM_GC
1788
/* check that enough storage for the new bag is available */
1789
if ( SizeAllocationArea < HEADER_SIZE+WORDS_BAG(new_size)
1790
&& CollectBags( new_size, 0 ) == 0 ) {
1791
return 0;
1792
}
1793
CLEAR_CANARY();
1794
/* allocate the storage for the bag */
1795
dst = AllocBags;
1796
AllocBags = dst + HEADER_SIZE + WORDS_BAG(new_size);
1797
ADD_CANARY();
1798
#else
1799
alloc_size = HEADER_SIZE*sizeof(Bag) + new_size;
1800
if (new_size == 0)
1801
alloc_size++;
1802
#ifndef DISABLE_GC
1803
dst = AllocateBagMemory(TabMarkTypeBags[type], type, alloc_size);
1804
#else
1805
dst = malloc( alloc_size );
1806
memset(dst, 0, alloc_size);
1807
#endif
1808
#endif
1809
1810
/* leave magic size-type word for the sweeper, type must be 255 */
1811
#ifdef USE_NEWSHAPE
1812
#ifndef BOEHM_GC
1813
*(*bag-2) = (((WORDS_BAG(old_size)+1) * sizeof(Bag))) << 16 | 255;
1814
#endif
1815
*dst++ = (Bag)(new_size << 16 | type);
1816
#else
1817
#ifndef BOEHM_GC
1818
*(*bag-3) = 255;
1819
*(*bag-2) = (((WORDS_BAG(old_size)+2) * sizeof(Bag)));
1820
#endif
1821
1822
/* enter the new size-type word */
1823
1824
*dst++ = (Bag)type;
1825
*dst++ = (Bag)new_size;
1826
#endif
1827
1828
1829
#ifndef BOEHM_GC
1830
CANARY_DISABLE_VALGRIND();
1831
/* if the bag is already on the changed bags list, keep it there */
1832
if ( PTR_BAG(bag)[-1] != bag ) {
1833
*dst++ = PTR_BAG(bag)[-1];
1834
}
1835
1836
/* if the bag is old, put it onto the changed bags list */
1837
else if ( PTR_BAG(bag) <= YoungBags ) {
1838
*dst++ = ChangedBags; ChangedBags = bag;
1839
}
1840
1841
/* if the bag is young, enter the normal link word */
1842
else {
1843
*dst++ = bag;
1844
}
1845
CANARY_ENABLE_VALGRIND();
1846
#else
1847
*dst++ = bag;
1848
#endif
1849
/* set the masterpointer */
1850
src = PTR_BAG(bag);
1851
#ifndef BOEHM_GC
1852
end = src + WORDS_BAG(old_size);
1853
#endif
1854
PTR_BAG(bag) = dst;
1855
1856
#ifndef BOEHM_GC
1857
/* copy the contents of the bag */
1858
while ( src < end )
1859
*dst++ = *src++;
1860
#else
1861
if (dst != src) {
1862
memcpy( dst, src, old_size < new_size ? old_size : new_size );
1863
} else if (new_size < old_size) {
1864
memset(dst+new_size, 0, old_size - new_size);
1865
}
1866
#endif
1867
1868
}
1869
1870
/* return success */
1871
return 1;
1872
}
1873
1874
1875
/****************************************************************************
1876
**
1877
*F CollectBags( <size>, <full> ) . . . . . . . . . . . . . collect dead bags
1878
**
1879
** 'CollectBags' is the function that does most of the work of {\Gasman}.
1880
**
1881
** A partial garbage collection where every bag is young is clearly a full
1882
** garbage collection. So to perform a full garbage collection,
1883
** 'CollectBags' first sets 'YoungBags' to 'OldBags', making every bag
1884
** young, and empties the list of changed old bags, since there are no old
1885
** bags anymore, there can be no changed old bags anymore. So from now on
1886
** we can assume that 'CollectBags' is doing a partial garbage
1887
** collection. In addition, the values 'NewWeakDeadBagMarker' and
1888
** 'OldWeakDeadBagMarker' are exchanged, so that bag idnetifiers that have
1889
** been halfdead since before this full garbage collection cab be
1890
** distinguished from those which have died on this pass.
1891
**
1892
** Garbage collection is performed in three phases. The mark phase, the
1893
** sweep phase, and the check phase.
1894
**
1895
** In the *mark phase*, 'CollectBags' finds all young bags that are still
1896
** live and builds a linked list of those bags (see "MarkedBags"). A bag is
1897
** put on this list of marked bags by applying 'MARK_BAG' to its
1898
** identifier. Note that 'MARK_BAG' checks that a bag is not already on the
1899
** list of marked bags, before it puts it on the list, so no bag can be put
1900
** twice on this list.
1901
**
1902
** First, 'CollectBags' marks all young bags that are directly accessible
1903
** through global variables, i.e., it marks those young bags whose
1904
** identifiers appear in global variables. It does this by applying
1905
** 'MARK_BAG' to the values at the addresses of global variables that may
1906
** hold bag identifiers provided by 'InitGlobalBag' (see "InitGlobalBag").
1907
**
1908
** Next, 'CollectBags' marks all young bags that are directly accessible
1909
** through local variables, i.e., it marks those young bags whose
1910
** identifiers appear in the stack. It does this by calling the stack
1911
** marking function <stack-func> (see "InitBags"). The generic stack
1912
** marking function, which is called if <stack-func> (see "InitBags") was 0,
1913
** is described below. The problem is that there is usually not sufficient
1914
** information available to decide if a value on the stack is really the
1915
** identifier of a bag, or is a value of another type that only appears to
1916
** be the identifier of a bag. The position usually taken by the stack
1917
** marking function is that everything on the stack that could possibly be
1918
** interpreted as the identifier of a bag is an identifier of a bag, and
1919
** that this bag is therefore live. This position is what makes {\Gasman} a
1920
** conservative storage manager.
1921
**
1922
** The generic stack marking function 'GenStackFuncBags', which is called if
1923
** <stack-func> (see "InitBags") was 0, works by applying 'MARK_BAG' to all
1924
** the values on the stack, which is supposed to extend from <stack-start>
1925
** (see "InitBags") to the address of a local variable of the function.
1926
** Note that some local variables may not be stored on the stack, because
1927
** they are still in the processors registers. 'GenStackFuncBags' uses a
1928
** jump buffer 'RegsBags', filled by the C library function 'setjmp', marking
1929
** all bags whose identifiers appear in 'RegsBags'. This is a dirty hack,
1930
** that need not work, but actually works on a surprisingly large number of
1931
** machines. But it will not work on Sun Sparc machines, which have larger
1932
** register files, of which only the part visible to the current function
1933
** will be saved by 'setjmp'. For those machines 'GenStackFuncBags' first
1934
** calls the operating system to flush the whole register file. Note that a
1935
** compiler may save a register somewhere else if it wants to use this
1936
** register for something else. Usually this register is saved further up
1937
** the stack, i.e., beyond the address of the local variable, and
1938
** 'GenStackFuncBags' would not see this value any more. To deal with this
1939
** problem, 'setjmp' must be called *before* 'GenStackFuncBags' is entered,
1940
** i.e., before the registers may have been saved elsewhere. Thus it is
1941
** called from 'CollectBags'.
1942
**
1943
** Next 'CollectBags' marks all young bags that are directly accessible from
1944
** old bags, i.e., it marks all young bags whose identifiers appear in the
1945
** data areas of old bags. It does this by applying 'MARK_BAG' to each
1946
** identifier appearing in changed old bags, i.e., in those bags that appear
1947
** on the list of changed old bags (see "ChangedBags"). To be more precise
1948
** it calls the marking function for the appropriate type to each changed
1949
** old bag (see "InitMarkFuncBags"). It need not apply the marking function
1950
** to each old bag, because old bags that have not been changed since the
1951
** last garbage collection cannot contain identifiers of young bags, which
1952
** have been allocated since the last garbage collection. Of course marking
1953
** the subbags of only the changed old bags is more efficient than marking
1954
** the subbags of all old bags only if the number of changed old bags is
1955
** smaller than the total number of old bags, but this is a very reasonable
1956
** assumption.
1957
**
1958
** Note that there may also be bags that appear to be young on the list of
1959
** changed old bags. Those bags are old bags that were extended since the
1960
** last garbage collection and therefore have their body in the young bags
1961
** area (see "Implementation of ResizeBag"). When 'CollectBags' finds such
1962
** a bag on the list of changed old bags it applies 'MARK_BAG' to its
1963
** identifier and thereby ensures that this bag will not be thrown away by
1964
** this garbage collection.
1965
**
1966
** Next, 'CollectBags' marks all young bags that are *indirectly*
1967
** accessible, i.e., it marks the subbags of the already marked bags, their
1968
** subbags and so on. It does so by walking along the list of already
1969
** marked bags and applies the marking function of the appropriate type to
1970
** each bag on this list (see "InitMarkFuncBags"). Those marking functions
1971
** then apply 'MARK_BAG' or 'MarkBagWeakly' to each identifier appearing in
1972
** the bag.
1973
**
1974
** After the marking function has been applied to a bag on the list of
1975
** marked bag, this bag is removed from the list. Thus the marking phase is
1976
** over when the list of marked bags has become empty. Removing the bag
1977
** from the list of marked bags must be done at this time, because newly
1978
** marked bags are *prepended* to the list of marked bags. This is done to
1979
** ensure that bags are marked in a depth first order, which should usually
1980
** improve locality of reference. When a bag is taken from the list of
1981
** marked bags it is *tagged*. This tag serves two purposes. A bag that is
1982
** tagged is not put on the list of marked bags when 'MARK_BAG' is applied
1983
** to its identifier. This ensures that no bag is put more than once onto
1984
** the list of marked bags, otherwise endless marking loops could happen for
1985
** structures that contain circular references. Also the sweep phase later
1986
** uses the presence of the tag to decide the status of the bag. There are
1987
** three possible statuses: LIVE, DEAD and HALFDEAD. The default state of a
1988
** bag with its identifier in the link word, is the tag for DEAD. Live bags
1989
** are tagged with MARKED_ALIVE(<identifier>) in the link word, and
1990
** half-dead bags (ie bags pointed to weakly but not strongly) with the tage
1991
** MARKED_HALFDEAD(<identifier>).
1992
**
1993
** Note that 'CollectBags' cannot put a random or magic value into the link
1994
** word, because the sweep phase must be able to find the masterpointer of a
1995
** bag by only looking at the link word of a bag. This is done using the macros
1996
** UNMARKED_XXX(<link word contents>).
1997
**
1998
** In the *sweep phase*, 'CollectBags' deallocates all dead bags and
1999
** compacts the live bags at the beginning of the workspace.
2000
**
2001
** In this phase 'CollectBags' uses a destination pointer 'dst', which
2002
** points to the address a body will be copied to, and a source pointer
2003
** 'src', which points to the address a body currently has. Both pointers
2004
** initially point to the beginning of the young bags area. Then
2005
** 'CollectBags' looks at the body pointed to by the source pointer.
2006
**
2007
** If this body has type 255, it is the remainder of a resize operation. In
2008
** this case 'CollectBags' simply moves the source pointer to the next body
2009
** (see "Implementation of ResizeBag").
2010
**
2011
**
2012
** Otherwise, if the link word contains the identifier of the bag itself,
2013
2014
** marked dead, 'CollectBags' first adds the masterpointer to the list of
2015
** available masterpointers (see "FreeMptrBags") and then simply moves the
2016
** source pointer to the next bag.
2017
**
2018
** Otherwise, if the link word contains the identifier of the bag marked
2019
** alive, this bag is still live. In this case 'CollectBags' calls the
2020
** sweeping function for this bag, if one is installed, or otherwise copies
2021
** the body from the source address to the destination address, stores the
2022
** address of the masterpointer without the tag in the link word, and
2023
** updates the masterpointer to point to the new address of the data area of
2024
** the bag. After the copying the source pointer points to the next bag,
2025
** and the destination pointer points just past the copy.
2026
**
2027
** Finally, if the link word contains the identifier of the bag marked half
2028
** dead, then 'CollectBags' puts the special value 'NewWeakDeadBagMarker'
2029
** into the masterpointer corresponding to the bag, to signify that this bag
2030
** has been collected as garbage.
2031
**
2032
** This is repeated until the source pointer reaches the end of the young
2033
** bags area, i.e., reaches 'AllocBags'.
2034
**
2035
** The new free storage now is the area between the destination pointer and
2036
** the source pointer. If the initialization flag <dirty> (see "InitBags")
2037
** was 0, this area is now cleared.
2038
**
2039
** Next, 'CollectBags' sets 'YoungBags' and 'AllocBags' to the address
2040
** pointed to by the destination pointer. So all the young bags that have
2041
** survived this garbage collection are now promoted to be old bags, and
2042
** allocation of new bags will start at the beginning of the free storage.
2043
**
2044
** Finally, the *check phase* checks whether the garbage collection freed
2045
** enough storage and masterpointers.
2046
**
2047
** After a partial garbage collection, 'CollectBags' wants at least '<size>
2048
** + AllocSizeBags' bytes of free storage available, where <size> is the
2049
** size of the bag that 'NewBag' is currently trying to allocate. Also the
2050
** number of free masterpointers should be larger than the number of bags
2051
** allocated since the previous garbage collection plus 4096 more to be
2052
** safe. If less free storage or fewer masterpointers are available,
2053
** 'CollectBags' calls itself for a full garbage collection.
2054
**
2055
** After a full garbage collection, 'CollectBags' wants at least <size>
2056
** bytes of free storage available, where <size> is the size of the bag that
2057
** 'NewBag' is currently trying to allocate. Also it wants at least one
2058
** free masterpointer. If less free storage or no masterpointer are
2059
** available, 'CollectBags' tries to extend the workspace using the
2060
** allocation function <alloc-func> (see "InitBags"). If <alloc-func>
2061
** refuses to extend the workspace, 'CollectBags' returns 0 to indicate
2062
** failure to 'NewBag'. In any case 'CollectBags' will try to extend the
2063
** workspace so that at least one eigth of the storage is free, that is, one
2064
** eight of the storage between 'OldBags' and 'EndBags' shall be free. If
2065
** <alloc-func> refuses this extension of the workspace, 'CollectBags' tries
2066
** to get along with what it got. Also 'CollectBags' wants at least one
2067
** masterpointer per 8 words of free storage available. If this is not the
2068
** case, 'CollectBags' extends the masterpointer area by moving the bodies
2069
** of all bags and readjusting the masterpointers.
2070
**
2071
** Also, after a full garbage collection, 'CollectBags' scans the
2072
** masterpointer area for identifiers containing 'OldWeakDeadBagMarker'. If
2073
** the sweep functions have done their work then no references to these bag
2074
** identifiers can exist, and so 'CollectBags' frees these masterpointers.
2075
*/
2076
#ifndef BOEHM_GC
2077
2078
syJmp_buf RegsBags;
2079
2080
#if defined(SPARC) && SPARC
2081
void SparcStackFuncBags( void )
2082
{
2083
asm (" ta 0x3 ");
2084
asm (" mov %sp,%o0" );
2085
return;
2086
}
2087
#endif
2088
2089
2090
void GenStackFuncBags ( void )
2091
{
2092
Bag * top; /* top of stack */
2093
Bag * p; /* loop variable */
2094
UInt i; /* loop variable */
2095
2096
top = (Bag*)((void*)&top);
2097
if ( StackBottomBags < top ) {
2098
for ( i = 0; i < sizeof(Bag*); i += StackAlignBags ) {
2099
for ( p = (Bag*)((char*)StackBottomBags + i); p < top; p++ )
2100
MARK_BAG( *p );
2101
}
2102
}
2103
else {
2104
for ( i = 0; i < sizeof(Bag*); i += StackAlignBags ) {
2105
for ( p = (Bag*)((char*)StackBottomBags - i); top < p; p-- )
2106
MARK_BAG( *p );
2107
}
2108
}
2109
#if ITANIUM
2110
/* Itanium has two stacks */
2111
top = ItaniumRegisterStackTop();
2112
for ( i = 0; i < sizeof(Bag*); i += StackAlignBags ) {
2113
for ( p = (Bag*)((char*)ItaniumRegisterStackBottom + i); p < top; p++ )
2114
MARK_BAG( *p );
2115
}
2116
#endif
2117
2118
/* mark from registers, dirty dirty hack */
2119
for ( p = (Bag*)((void*)RegsBags);
2120
p < (Bag*)((void*)RegsBags)+sizeof(RegsBags)/sizeof(Bag);
2121
p++ )
2122
MARK_BAG( *p );
2123
2124
}
2125
2126
UInt FullBags;
2127
2128
/* These are used to overwrite masterpointers which may still be
2129
linked from weak pointer objects but whose bag bodies have been
2130
collected. Two values are used so that old masterpointers of this
2131
kind can be reclaimed after a full garbage collection. The values must
2132
not look like valid pointers, and should be congruent to 1 mod sizeof(Bag) */
2133
2134
Bag * NewWeakDeadBagMarker = (Bag *)(1000*sizeof(Bag) + 1L);
2135
Bag * OldWeakDeadBagMarker = (Bag *)(1001*sizeof(Bag) + 1L);
2136
2137
#endif /* !BOEHM_GC */
2138
2139
2140
2141
UInt CollectBags (
2142
UInt size,
2143
UInt full )
2144
{
2145
#ifndef BOEHM_GC
2146
Bag first; /* first bag on a linked list */
2147
Bag * p; /* loop variable */
2148
Bag * dst; /* destination in sweeping */
2149
Bag * src; /* source in sweeping */
2150
Bag * end; /* end of a bag in sweeping */
2151
UInt nrLiveBags; /* number of live new bags */
2152
UInt sizeLiveBags; /* total size of live new bags */
2153
UInt nrDeadBags; /* number of dead new bags */
2154
UInt nrHalfDeadBags; /* number of dead new bags */
2155
UInt sizeDeadBags; /* total size of dead new bags */
2156
UInt done; /* do we have to make a full gc */
2157
UInt i; /* loop variable */
2158
2159
/* Bag * last;
2160
Char type; */
2161
2162
CANARY_DISABLE_VALGRIND();
2163
CLEAR_CANARY();
2164
#ifdef DEBUG_MASTERPOINTERS
2165
CheckMasterPointers();
2166
#endif
2167
2168
2169
/* call the before function (if any) */
2170
if ( BeforeCollectFuncBags != 0 )
2171
(*BeforeCollectFuncBags)();
2172
2173
/* copy 'full' into a global variable, to avoid warning from GNU C */
2174
FullBags = full;
2175
2176
/* do we want to make a full garbage collection? */
2177
again:
2178
if ( FullBags ) {
2179
2180
/* then every bag is considered to be a young bag */
2181
YoungBags = OldBags;
2182
NrLiveBags = 0;
2183
SizeLiveBags = 0;
2184
2185
/* empty the list of changed old bags */
2186
while ( ChangedBags != 0 ) {
2187
first = ChangedBags;
2188
ChangedBags = PTR_BAG(first)[-1];
2189
PTR_BAG(first)[-1] = first;
2190
}
2191
2192
/* Also time to change the tag for dead children of weak
2193
pointer objects. After this collection, there can be no more
2194
weak pointer objects pointing to anything with OldWeakDeadBagMarker
2195
in it */
2196
{
2197
Bag * t;
2198
t = OldWeakDeadBagMarker;
2199
OldWeakDeadBagMarker = NewWeakDeadBagMarker;
2200
NewWeakDeadBagMarker = t;
2201
}
2202
}
2203
2204
/* information at the beginning of garbage collections */
2205
if ( MsgsFuncBags )
2206
(*MsgsFuncBags)( FullBags, 0, 0 );
2207
2208
/* * * * * * * * * * * * * * * mark phase * * * * * * * * * * * * * * */
2209
2210
/* prepare the list of marked bags for the future */
2211
MarkedBags = 0;
2212
2213
/* mark from the static area */
2214
for ( i = 0; i < GlobalBags.nr; i++ )
2215
MARK_BAG( *GlobalBags.addr[i] );
2216
2217
2218
/* mark from the stack */
2219
if ( StackFuncBags ) {
2220
(*StackFuncBags)();
2221
}
2222
else {
2223
sySetjmp( RegsBags );
2224
#ifdef SPARC
2225
#if SPARC
2226
SparcStackFuncBags();
2227
#endif
2228
#endif
2229
GenStackFuncBags();
2230
}
2231
2232
/* mark the subbags of the changed old bags */
2233
while ( ChangedBags != 0 ) {
2234
first = ChangedBags;
2235
ChangedBags = PTR_BAG(first)[-1];
2236
PTR_BAG(first)[-1] = first;
2237
if ( PTR_BAG(first) <= YoungBags )
2238
(*TabMarkFuncBags[TNUM_BAG(first)])( first );
2239
else
2240
MARK_BAG(first);
2241
}
2242
2243
2244
/* tag all marked bags and mark their subbags */
2245
nrLiveBags = 0;
2246
sizeLiveBags = 0;
2247
while ( MarkedBags != 0 ) {
2248
first = MarkedBags;
2249
MarkedBags = PTR_BAG(first)[-1];
2250
PTR_BAG(first)[-1] = MARKED_ALIVE(first);
2251
(*TabMarkFuncBags[TNUM_BAG(first)])( first );
2252
nrLiveBags++;
2253
sizeLiveBags += SIZE_BAG(first);
2254
}
2255
2256
/* information after the mark phase */
2257
NrLiveBags += nrLiveBags;
2258
if ( MsgsFuncBags )
2259
(*MsgsFuncBags)( FullBags, 1, nrLiveBags );
2260
SizeLiveBags += sizeLiveBags;
2261
if ( MsgsFuncBags )
2262
(*MsgsFuncBags)( FullBags, 2, sizeLiveBags/1024 );
2263
2264
/* * * * * * * * * * * * * * * sweep phase * * * * * * * * * * * * * * */
2265
2266
#if 0
2267
/* call freeing function for all dead bags */
2268
if ( NrTabFreeFuncBags ) {
2269
2270
/* run through the young generation */
2271
src = YoungBags;
2272
while ( src < AllocBags ) {
2273
2274
/* leftover of a resize of <n> bytes */
2275
if ( (*(UInt*)src & 0xFFL) == 255 ) {
2276
2277
if ((*(UInt *)src >> 16) == 1)
2278
src++;
2279
else
2280
src += WORDS_BAG(((UInt *)src)[1]);
2281
2282
2283
}
2284
2285
/* dead or half-dead (only weakly pointed to bag */
2286
/* here the usual check using UNMARKED_DEAD etc. is not
2287
safe, because we are looking at the bag body rather
2288
than its identifier, and a wrong guess for the bag
2289
status can involve following a misaligned pointer. It
2290
may cause bus errors or actual mistakes.
2291
2292
Instead we look directly at the value in the link word
2293
and check its least significant bits */
2294
2295
else if ( ((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 0 ||
2296
((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 2 )
2297
{
2298
#ifdef DEBUG_MASTERPOINTERS
2299
if ( (((UInt)(src[1])) % sizeof(Bag) == 0 &&
2300
PTR_BAG( UNMARKED_DEAD(src[1]) ) != src+HEADER_SIZE) ||
2301
(((UInt)(src[1])) % sizeof(Bag) == 2 &&
2302
PTR_BAG( UNMARKED_HALFDEAD(src[1])) != src+HEADER_SIZE))
2303
{
2304
(*AbortFuncBags)("incorrectly marked bag");
2305
}
2306
#endif
2307
2308
/* call freeing function */
2309
if ( TabFreeFuncBags[ *(UInt*)src & 0xFFL ] != 0 )
2310
(*TabFreeFuncBags[ *(UInt*)src & 0xFFL ])( src[HEADER_SIZE-1] );
2311
2312
/* advance src */
2313
src += HEADER_SIZE + WORDS_BAG( ((UInt*)src)[1] ) ;
2314
2315
}
2316
2317
2318
/* live bag */
2319
else if ( ((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 1 )
2320
{
2321
#ifdef DEBUG_MASTERPOINTERS
2322
if ( PTR_BAG( UNMARKED_ALIVE(src[HEADER_SIZE-1]) ) != src+HEADER_SIZE )
2323
{
2324
(*AbortFuncBags)("incorrectly marked bag");
2325
}
2326
#endif
2327
2328
/* advance src */
2329
#ifdef USE_NEWSHAPE
2330
src += HEADER_SIZE + WORDS_BAG( ((UInt*)src)[0] >>16 );
2331
#else
2332
src += HEADER_SIZE + WORDS_BAG( ((UInt*)src)[1] );
2333
#endif
2334
2335
2336
}
2337
2338
/* oops */
2339
else {
2340
(*AbortFuncBags)(
2341
"Panic: Gasman found a bogus header (looking for dead bags)");
2342
}
2343
2344
}
2345
2346
}
2347
#endif
2348
/* sweep through the young generation */
2349
nrDeadBags = 0;
2350
nrHalfDeadBags = 0;
2351
sizeDeadBags = 0;
2352
dst = YoungBags;
2353
src = YoungBags;
2354
while ( src < AllocBags ) {
2355
2356
/* leftover of a resize of <n> bytes */
2357
if ( (*(UInt*)src & 0xFFL) == 255 ) {
2358
2359
/* advance src */
2360
if ((*(UInt *) src) >> 8 == 1)
2361
src++;
2362
else
2363
#ifdef USE_NEWSHAPE
2364
src += 1 + WORDS_BAG(((UInt *)src)[0] >> 16);
2365
#else
2366
src += 1 + WORDS_BAG(((UInt *)src)[1]);
2367
#endif
2368
2369
}
2370
2371
/* dead bag */
2372
2373
else if ( ((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 0 )
2374
{
2375
#ifdef DEBUG_MASTERPOINTERS
2376
if ( PTR_BAG( UNMARKED_DEAD(src[HEADER_SIZE-1]) ) != src+HEADER_SIZE )
2377
{
2378
(*AbortFuncBags)("incorrectly marked bag");
2379
}
2380
#endif
2381
2382
2383
/* update count */
2384
if (TabFreeFuncBags[ *(UInt *)src & 0xFFL] != 0)
2385
(*TabFreeFuncBags[ *(UInt*)src & 0xFFL ])( src[HEADER_SIZE-1] );
2386
nrDeadBags += 1;
2387
#ifdef USE_NEWSHAPE
2388
sizeDeadBags += ((UInt *)src)[0] >> 16;
2389
#else
2390
sizeDeadBags += ((UInt *)src)[1];
2391
#endif
2392
2393
#ifdef COUNT_BAGS
2394
/* update the statistics */
2395
InfoBags[*(UInt*)src & 0xFFL].nrLive -= 1;
2396
#ifdef USE_NEWSHAPE
2397
InfoBags[*(UInt*)src & 0xFFL].sizeLive -=
2398
((UInt *)src)[0] >>16;
2399
#else
2400
InfoBags[*(UInt*)src & 0xFFL].sizeLive -=
2401
((UInt *)src)[1];
2402
#endif
2403
#endif
2404
2405
/* free the identifier */
2406
*(Bag*)(src[HEADER_SIZE-1]) = FreeMptrBags;
2407
FreeMptrBags = src[HEADER_SIZE-1];
2408
2409
/* advance src */
2410
#ifdef USE_NEWSHAPE
2411
src += HEADER_SIZE +
2412
WORDS_BAG( ((UInt*)src)[0] >> 16 ) ;
2413
#else
2414
src += HEADER_SIZE +
2415
WORDS_BAG( ((UInt*)src)[1] ) ;
2416
#endif
2417
2418
}
2419
2420
/* half-dead bag */
2421
else if ( ((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 2 )
2422
{
2423
#ifdef DEBUG_MASTERPOINTERS
2424
if ( PTR_BAG( UNMARKED_HALFDEAD(src[HEADER_SIZE-1]) ) != src+HEADER_SIZE )
2425
{
2426
(*AbortFuncBags)("incorrectly marked bag");
2427
}
2428
#endif
2429
2430
2431
/* update count */
2432
nrDeadBags += 1;
2433
#ifdef USE_NEWSHAPE
2434
sizeDeadBags += ((UInt *)src)[0] >> 16;
2435
#else
2436
sizeDeadBags += ((UInt *)src)[1];
2437
#endif
2438
2439
#ifdef COUNT_BAGS
2440
/* update the statistics */
2441
InfoBags[*(UInt*)src & 0xFFL].nrLive -= 1;
2442
#ifdef USE_NEWSHAPE
2443
InfoBags[*(UInt*)src & 0xFFL].sizeLive -=
2444
((UInt *)src)[0] >>16;
2445
#else
2446
InfoBags[*(UInt*)src & 0xFFL].sizeLive -=
2447
((UInt *)src)[1];
2448
#endif
2449
#endif
2450
2451
/* don't free the identifier */
2452
if (((UInt)UNMARKED_HALFDEAD(src[HEADER_SIZE-1])) % 4 != 0)
2453
(*AbortFuncBags)("align error in halfdead bag");
2454
2455
*(Bag**)(UNMARKED_HALFDEAD(src[HEADER_SIZE-1])) = NewWeakDeadBagMarker;
2456
nrHalfDeadBags ++;
2457
2458
/* advance src */
2459
#ifdef USE_NEWSHAPE
2460
src += HEADER_SIZE +
2461
WORDS_BAG( ((UInt*)src)[0] >> 16 ) ;
2462
#else
2463
src += HEADER_SIZE +
2464
WORDS_BAG( ((UInt*)src)[1] ) ;
2465
#endif
2466
2467
}
2468
2469
/* live bag */
2470
else if ( ((UInt)(src[HEADER_SIZE-1])) % sizeof(Bag) == 1 )
2471
{
2472
#ifdef DEBUG_MASTERPOINTERS
2473
if ( PTR_BAG( UNMARKED_ALIVE(src[HEADER_SIZE-1]) ) != src+HEADER_SIZE )
2474
{
2475
(*AbortFuncBags)("incorrectly marked bag");
2476
}
2477
#endif
2478
2479
2480
/* update identifier, copy size-type and link field */
2481
PTR_BAG( UNMARKED_ALIVE(src[HEADER_SIZE-1])) = dst+HEADER_SIZE;
2482
#ifdef USE_NEWSHAPE
2483
end = src + HEADER_SIZE +
2484
WORDS_BAG( ((UInt*)src)[0] >>16 ) ;
2485
#else
2486
end = src + HEADER_SIZE +
2487
WORDS_BAG( ((UInt*)src)[1] ) ;
2488
#endif
2489
*dst++ = *src++;
2490
#ifndef USE_NEWSHAPE
2491
*dst++ = *src++;
2492
#endif
2493
2494
*dst++ = (Bag)UNMARKED_ALIVE(*src++);
2495
2496
/* copy data area */
2497
if (TabSweepFuncBags[(UInt)(src[-HEADER_SIZE]) & 0xFFL] != 0)
2498
{
2499
/* Call the installed sweeping function */
2500
(*(TabSweepFuncBags[(UInt)(src[-HEADER_SIZE]) & 0xFFL]))(src,dst,end-src);
2501
dst += end-src;
2502
src = end;
2503
2504
}
2505
2506
/* Otherwise do the default thing */
2507
else if ( dst != src ) {
2508
memmove((void *)dst, (void *)src, (end - src)*sizeof(*src));
2509
dst += (end-src);
2510
src = end;
2511
2512
/*
2513
while ( src < end )
2514
*dst++ = *src++;
2515
*/
2516
}
2517
else {
2518
dst = end;
2519
src = end;
2520
}
2521
}
2522
2523
/* oops */
2524
else {
2525
2526
(*AbortFuncBags)("Panic: Gasman found a bogus header");
2527
2528
}
2529
2530
}
2531
2532
/* reset the pointer to the free storage */
2533
AllocBags = YoungBags = dst;
2534
2535
/* clear the new free area */
2536
if (!DirtyBags)
2537
memset((void *)dst, 0, ((Char *)src)-((Char *)dst));
2538
2539
/* if ( ! DirtyBags ) {
2540
while ( dst < src )
2541
*dst++ = 0;
2542
} */
2543
2544
/* information after the sweep phase */
2545
NrDeadBags += nrDeadBags;
2546
NrHalfDeadBags += nrHalfDeadBags;
2547
if ( MsgsFuncBags )
2548
(*MsgsFuncBags)( FullBags, 3,
2549
(FullBags ? NrDeadBags:nrDeadBags) );
2550
if ( FullBags )
2551
NrDeadBags = 0;
2552
SizeDeadBags += sizeDeadBags;
2553
if ( MsgsFuncBags )
2554
(*MsgsFuncBags)( FullBags, 4,
2555
(FullBags ? SizeDeadBags:sizeDeadBags)/1024 );
2556
if ( FullBags )
2557
SizeDeadBags = 0;
2558
2559
/* * * * * * * * * * * * * * * check phase * * * * * * * * * * * * * * */
2560
2561
/* temporarily store in 'StopBags' where this allocation takes us */
2562
StopBags = AllocBags + HEADER_SIZE + WORDS_BAG(size);
2563
2564
2565
2566
/* if we only performed a partial garbage collection */
2567
if ( ! FullBags ) {
2568
2569
/* maybe adjust the size of the allocation area */
2570
if ( ! CacheSizeBags ) {
2571
if ( nrLiveBags+nrDeadBags +nrHalfDeadBags < 512
2572
2573
/* The test below should stop AllocSizeBags
2574
growing uncontrollably when all bags are big */
2575
&& StopBags > OldBags + 4*1024*WORDS_BAG(AllocSizeBags))
2576
AllocSizeBags += 256L;
2577
else if ( 4096 < nrLiveBags+nrDeadBags+nrHalfDeadBags
2578
&& 256 < AllocSizeBags )
2579
AllocSizeBags -= 256;
2580
}
2581
else {
2582
if ( nrLiveBags+nrDeadBags < 512 )
2583
AllocSizeBags += CacheSizeBags/1024;
2584
else if ( 4096 < nrLiveBags+nrDeadBags+nrHalfDeadBags
2585
&& CacheSizeBags < AllocSizeBags )
2586
AllocSizeBags -= CacheSizeBags/1024;
2587
}
2588
2589
/* if we dont get enough free storage or masterpointers do full gc */
2590
if ( EndBags < StopBags + WORDS_BAG(1024*AllocSizeBags)
2591
|| SizeMptrsArea <
2592
2593
/* nrLiveBags+nrDeadBags+nrHalfDeadBags+ 4096 */
2594
/* If this test triggered, but the one below didn't
2595
then a full collection would ensue which wouldn't
2596
do anything useful. Possibly a version of the
2597
above test should be moved into the full collection also
2598
but I wasn't sure it always made sense SL */
2599
2600
/* change the test to avoid subtracting unsigned integers */
2601
2602
WORDS_BAG(AllocSizeBags*1024)/7 +(NrLiveBags + NrHalfDeadBags)
2603
) {
2604
done = 0;
2605
}
2606
else {
2607
done = 1;
2608
}
2609
2610
}
2611
2612
/* if we already performed a full garbage collection */
2613
else {
2614
2615
/* Clean up old half-dead bags
2616
also reorder the free masterpointer linked list
2617
to get more locality */
2618
FreeMptrBags = (Bag)0L;
2619
for (p = MptrBags; p < OldBags; p+= SIZE_MPTR_BAGS)
2620
{
2621
Bag *mptr = (Bag *)*p;
2622
if ( mptr == OldWeakDeadBagMarker)
2623
NrHalfDeadBags--;
2624
if ( mptr == OldWeakDeadBagMarker || IS_BAG((UInt)mptr) || mptr == 0)
2625
{
2626
*p = FreeMptrBags;
2627
FreeMptrBags = (Bag)p;
2628
}
2629
}
2630
2631
2632
/* get the storage we absolutly need */
2633
while ( EndBags < StopBags
2634
&& (*AllocFuncBags)(512,1) )
2635
EndBags += WORDS_BAG(512*1024L);
2636
2637
/* if not enough storage is free, fail */
2638
if ( EndBags < StopBags )
2639
return 0;
2640
2641
/* if less than 1/8th is free, get more storage (in 1/2 MBytes) */
2642
while ( ( SpaceBetweenPointers(EndBags, StopBags) < SpaceBetweenPointers(StopBags, OldBags)/7 ||
2643
SpaceBetweenPointers(EndBags, StopBags) < WORDS_BAG(AllocSizeBags) )
2644
&& (*AllocFuncBags)(512,0) )
2645
EndBags += WORDS_BAG(512*1024L);
2646
2647
/* If we are having trouble, then cut our cap to fit our cloth *.
2648
if ( EndBags - StopBags < AllocSizeBags )
2649
AllocSizeBags = 7*(Endbags - StopBags)/8; */
2650
2651
/* if less than 1/16th is free, prepare for an interrupt */
2652
if (SpaceBetweenPointers(StopBags,OldBags)/15 < SpaceBetweenPointers(EndBags,StopBags) ) {
2653
/*N 1993/05/16 martin must change 'gap.c' */
2654
;
2655
}
2656
2657
/* if more than 1/8th is free, give back storage (in 1/2 MBytes) */
2658
while (SpaceBetweenPointers(StopBags,OldBags)/7 <= SpaceBetweenPointers(EndBags,StopBags)-WORDS_BAG(512*1024L)
2659
&& SpaceBetweenPointers(EndBags,StopBags) > WORDS_BAG(AllocSizeBags) + WORDS_BAG(512*1024L)
2660
&& (*AllocFuncBags)(-512,0) )
2661
EndBags -= WORDS_BAG(512*1024L);
2662
2663
/* if we want to increase the masterpointer area */
2664
if ( SpaceBetweenPointers(OldBags,MptrBags)-NrLiveBags < SpaceBetweenPointers(EndBags,StopBags)/7 ) {
2665
2666
/* this is how many new masterpointers we want */
2667
i = SpaceBetweenPointers(EndBags,StopBags)/7 - (SpaceBetweenPointers(OldBags,MptrBags)-NrLiveBags);
2668
2669
/* move the bags area */
2670
memmove((void *)(OldBags+i), (void *)OldBags, SpaceBetweenPointers(AllocBags,OldBags)*sizeof(*OldBags));
2671
2672
/* update the masterpointers */
2673
for ( p = MptrBags; p < OldBags; p++ ) {
2674
if ( (Bag)OldBags <= *p)
2675
*p += i;
2676
}
2677
2678
/* link the new part of the masterpointer area */
2679
for ( p = OldBags;
2680
p + 2*SIZE_MPTR_BAGS <= OldBags+i;
2681
p += SIZE_MPTR_BAGS ) {
2682
*p = (Bag)(p + SIZE_MPTR_BAGS);
2683
}
2684
*p = (Bag)FreeMptrBags;
2685
FreeMptrBags = (Bag)OldBags;
2686
2687
/* update 'OldBags', 'YoungBags', 'AllocBags', and 'StopBags' */
2688
OldBags += i;
2689
YoungBags += i;
2690
AllocBags += i;
2691
StopBags += i;
2692
2693
}
2694
2695
/* now we are done */
2696
done = 1;
2697
2698
}
2699
2700
/* information after the check phase */
2701
if ( MsgsFuncBags )
2702
(*MsgsFuncBags)( FullBags, 5,
2703
SpaceBetweenPointers(EndBags, StopBags)/(1024/sizeof(Bag)));
2704
if ( MsgsFuncBags )
2705
(*MsgsFuncBags)( FullBags, 6,
2706
SpaceBetweenPointers(EndBags, MptrBags)/(1024/sizeof(Bag)));
2707
2708
/* reset the stop pointer */
2709
if ( ! CacheSizeBags || EndBags < StopBags+WORDS_BAG(1024*AllocSizeBags) )
2710
StopBags = EndBags;
2711
else
2712
StopBags = StopBags + WORDS_BAG(1024*AllocSizeBags);
2713
2714
/* if we are not done, then true again */
2715
if ( ! done ) {
2716
FullBags = 1;
2717
goto again;
2718
}
2719
2720
/* call the after function (if any) */
2721
if ( AfterCollectFuncBags != 0 )
2722
(*AfterCollectFuncBags)();
2723
2724
2725
#ifdef DEBUG_MASTERPOINTERS
2726
CheckMasterPointers();
2727
#endif
2728
2729
/* Possibly advise the operating system about unused pages: */
2730
SyMAdviseFree();
2731
2732
CANARY_ENABLE_VALGRIND();
2733
2734
/* return success */
2735
return 1;
2736
#else
2737
#ifndef DISABLE_GC
2738
GC_gcollect();
2739
#endif
2740
return 1;
2741
#endif
2742
}
2743
2744
2745
/****************************************************************************
2746
**
2747
*F CheckMasterPointers() . . . . do consistency checks on the masterpointers
2748
**
2749
*/
2750
2751
#ifndef BOEHM_GC
2752
void CheckMasterPointers( void )
2753
{
2754
Bag *ptr;
2755
for (ptr = MptrBags; ptr < OldBags; ptr++)
2756
{
2757
if (*ptr != (Bag)0 && /* bottom of free chain */
2758
*ptr != (Bag)NewWeakDeadBagMarker &&
2759
*ptr != (Bag)OldWeakDeadBagMarker &&
2760
(((Bag *)*ptr < MptrBags &&
2761
(Bag *)*ptr > AllocBags) ||
2762
(UInt)(*ptr) % sizeof(Bag) != 0))
2763
(*AbortFuncBags)("Bad master pointer detected in check");
2764
}
2765
}
2766
#endif
2767
2768
2769
/****************************************************************************
2770
**
2771
*F SwapMasterPoint( <bag1>, <bag2> ) . . . swap pointer of <bag1> and <bag2>
2772
*/
2773
void SwapMasterPoint (
2774
Bag bag1,
2775
Bag bag2 )
2776
{
2777
Bag * ptr1;
2778
Bag * ptr2;
2779
2780
if ( bag1 == bag2 )
2781
return;
2782
2783
/* get the pointers */
2784
ptr1 = PTR_BAG(bag1);
2785
ptr2 = PTR_BAG(bag2);
2786
2787
/* check and update the link field and changed bags */
2788
if ( PTR_BAG(bag1)[-1] == bag1 && PTR_BAG(bag2)[-1] == bag2 ) {
2789
PTR_BAG(bag1)[-1] = bag2;
2790
PTR_BAG(bag2)[-1] = bag1;
2791
}
2792
else if ( PTR_BAG(bag1)[-1] == bag1 ) {
2793
PTR_BAG(bag1)[-1] = ChangedBags;
2794
ChangedBags = bag1;
2795
}
2796
else if ( PTR_BAG(bag2)[-1] == bag2 ) {
2797
PTR_BAG(bag2)[-1] = ChangedBags;
2798
ChangedBags = bag2;
2799
}
2800
2801
/* swap them */
2802
PTR_BAG(bag1) = ptr2;
2803
PTR_BAG(bag2) = ptr1;
2804
}
2805
2806
2807
2808
/****************************************************************************
2809
**
2810
2811
*F BID(<bag>) . . . . . . . . . . . . bag identifier (as unsigned integer)
2812
*F IS_BAG(<bid>) . . . . . . test whether a bag identifier identifies a bag
2813
*F BAG(<bid>) . . . . . . . . . . . . . . . . . . bag (from bag identifier)
2814
*F TNUM_BAG(<bag>) . . . . . . . . . . . . . . . . . . . . . . type of a bag
2815
*F SIZE_BAG(<bag>) . . . . . . . . . . . . . . . . . . . . . . size of a bag
2816
*F PTR_BAG(<bag>) . . . . . . . . . . . . . . . . . . . . pointer to a bag
2817
*F ELM_BAG(<bag>,<i>) . . . . . . . . . . . . . . . <i>-th element of a bag
2818
*F SET_ELM_BAG(<bag>,<i>,<elm>) . . . . . . . . set <i>-th element of a bag
2819
**
2820
** 'BID', 'IS_BAG', 'BAG', 'TNUM_BAG', 'TNAM_BAG', 'PTR_BAG', 'ELM_BAG', and
2821
** 'SET_ELM_BAG' are functions to support debugging. They are not intended
2822
** to be used in an application using {\Gasman}. Note that the functions
2823
** 'TNUM_BAG', 'SIZE_BAG', and 'PTR_BAG' shadow the macros of the same name,
2824
** which are usually not available in a debugger.
2825
*/
2826
2827
#ifdef DEBUG_FUNCTIONS_BAGS
2828
2829
#undef TNUM_BAG
2830
#undef SIZE_BAG
2831
#undef PTR_BAG
2832
2833
UInt BID (
2834
Bag bag )
2835
{
2836
return (UInt) bag;
2837
}
2838
2839
2840
Bag BAG (
2841
UInt bid )
2842
{
2843
if ( IS_BAG(bid) )
2844
return (Bag) bid;
2845
else
2846
return (Bag) 0;
2847
}
2848
2849
UInt TNUM_BAG (
2850
Bag bag )
2851
{
2852
return (*(*(bag)-3) & 0xFFL);
2853
}
2854
2855
const Char * TNAM_BAG (
2856
Bag bag )
2857
{
2858
return InfoBags[ (*(*(bag)-3) & 0xFFL) ].name;
2859
}
2860
2861
UInt SIZE_BAG (
2862
Bag bag )
2863
{
2864
return (*(*(bag)-2));
2865
}
2866
2867
Bag * PTR_BAG (
2868
Bag bag )
2869
{
2870
return (*(Bag**)(bag));
2871
}
2872
2873
UInt ELM_BAG (
2874
Bag bag,
2875
UInt i )
2876
{
2877
return (UInt) ((*(Bag**)(bag))[i]);
2878
}
2879
2880
UInt SET_ELM_BAG (
2881
Bag bag,
2882
UInt i,
2883
UInt elm )
2884
{
2885
(*(Bag**)(bag))[i] = (Bag) elm;
2886
return elm;
2887
}
2888
2889
#endif
2890
2891
2892
/****************************************************************************
2893
**
2894
*E gasman.c . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here
2895
*/
2896
2897