aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code/trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'mps/code/trace.c')
-rw-r--r--mps/code/trace.c1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/mps/code/trace.c b/mps/code/trace.c
new file mode 100644
index 00000000000..619e67480f0
--- /dev/null
+++ b/mps/code/trace.c
@@ -0,0 +1,1666 @@
1/* impl.c.trace: GENERIC TRACER IMPLEMENTATION
2 *
3 * $HopeName: MMsrc!trace.c(trunk.102) $
4 * Copyright (C) 2001 Harlequin Limited. All rights reserved.
5 *
6 * .design: design.mps.trace. */
7
8#include "chain.h"
9#include "mpm.h"
10#include <limits.h> /* for LONG_MAX */
11
12SRCID(trace, "$HopeName: MMsrc!trace.c(trunk.102) $");
13
14
15/* Types */
16
17enum {traceAccountingPhaseRootScan = 1, traceAccountingPhaseSegScan,
18 traceAccountingPhaseSingleScan};
19typedef int traceAccountingPhase;
20
21
22/* TraceMessage -- type of GC end messages */
23
24#define TraceMessageSig ((Sig)0x51926359)
25
26typedef struct TraceMessageStruct {
27 Sig sig;
28 Size liveSize;
29 Size condemnedSize;
30 Size notCondemnedSize;
31 MessageStruct messageStruct;
32} TraceMessageStruct, *TraceMessage;
33
34#define TraceMessageMessage(TraceMessage) (&((TraceMessage)->messageStruct))
35#define MessageTraceMessage(message) \
36 (PARENT(TraceMessageStruct, messageStruct, message))
37
38static Bool TraceMessageCheck(TraceMessage message)
39{
40 CHECKS(TraceMessage, message);
41 CHECKD(Message, TraceMessageMessage(message));
42 CHECKL(MessageGetType(TraceMessageMessage(message)) ==
43 MessageTypeGC);
44 /* We can't check anything about the statistics. In particular, */
45 /* liveSize may exceed condemnedSize because they are only estimates. */
46
47 return TRUE;
48}
49
50static void TraceMessageDelete(Message message)
51{
52 TraceMessage tMessage;
53 Arena arena;
54
55 AVERT(Message, message);
56 tMessage = MessageTraceMessage(message);
57 AVERT(TraceMessage, tMessage);
58
59 arena = MessageArena(message);
60 ControlFree(arena, (void *)tMessage, sizeof(TraceMessageStruct));
61}
62
63static Size TraceMessageLiveSize(Message message)
64{
65 TraceMessage tMessage;
66
67 AVERT(Message, message);
68 tMessage = MessageTraceMessage(message);
69 AVERT(TraceMessage, tMessage);
70
71 return tMessage->liveSize;
72}
73
74static Size TraceMessageCondemnedSize(Message message)
75{
76 TraceMessage tMessage;
77
78 AVERT(Message, message);
79 tMessage = MessageTraceMessage(message);
80 AVERT(TraceMessage, tMessage);
81
82 return tMessage->condemnedSize;
83}
84
85static Size TraceMessageNotCondemnedSize(Message message)
86{
87 TraceMessage tMessage;
88
89 AVERT(Message, message);
90 tMessage = MessageTraceMessage(message);
91 AVERT(TraceMessage, tMessage);
92
93 return tMessage->notCondemnedSize;
94}
95
96static MessageClassStruct TraceMessageClassStruct = {
97 MessageClassSig, /* sig */
98 "TraceGC", /* name */
99 TraceMessageDelete, /* Delete */
100 MessageNoFinalizationRef, /* FinalizationRef */
101 TraceMessageLiveSize, /* GCLiveSize */
102 TraceMessageCondemnedSize, /* GCCondemnedSize */
103 TraceMessageNotCondemnedSize, /* GCNotCondemnedSize */
104 MessageClassSig /* design.mps.message.class.sig.double */
105};
106
107static void TraceMessageInit(Arena arena, TraceMessage tMessage)
108{
109 AVERT(Arena, arena);
110
111 MessageInit(arena, TraceMessageMessage(tMessage),
112 &TraceMessageClassStruct, MessageTypeGC);
113 tMessage->liveSize = (Size)0;
114 tMessage->condemnedSize = (Size)0;
115 tMessage->notCondemnedSize = (Size)0;
116
117 tMessage->sig = TraceMessageSig;
118 AVERT(TraceMessage, tMessage);
119}
120
121
122/* ScanStateCheck -- check consistency of a ScanState object */
123
124Bool ScanStateCheck(ScanState ss)
125{
126 TraceId ti;
127 Trace trace;
128 ZoneSet white;
129
130 CHECKS(ScanState, ss);
131 CHECKL(FUNCHECK(ss->fix));
132 CHECKL(ss->zoneShift == ss->arena->zoneShift);
133 white = ZoneSetEMPTY;
134 TRACE_SET_ITER(ti, trace, ss->traces, ss->arena)
135 white = ZoneSetUnion(white, ss->arena->trace[ti].white);
136 TRACE_SET_ITER_END(ti, trace, ss->traces, ss->arena);
137 CHECKL(ss->white == white);
138 CHECKU(Arena, ss->arena);
139 /* Summaries could be anything, and can't be checked. */
140 CHECKL(TraceSetCheck(ss->traces));
141 CHECKL(TraceSetSuper(ss->arena->busyTraces, ss->traces));
142 CHECKL(RankCheck(ss->rank));
143 CHECKL(BoolCheck(ss->wasMarked));
144 /* @@@@ checks for counts missing */
145 return TRUE;
146}
147
148
149/* ScanStateInit -- Initialize a ScanState object */
150
151void ScanStateInit(ScanState ss, TraceSet ts, Arena arena,
152 Rank rank, ZoneSet white)
153{
154 TraceId ti;
155 Trace trace;
156
157 AVER(TraceSetCheck(ts));
158 AVERT(Arena, arena);
159 AVER(RankCheck(rank));
160 /* white is arbitrary and can't be checked */
161
162 ss->fix = TraceFix;
163 TRACE_SET_ITER(ti, trace, ts, arena)
164 if (trace->emergency)
165 ss->fix = TraceFixEmergency;
166 TRACE_SET_ITER_END(ti, trace, ts, arena);
167 ss->rank = rank;
168 ss->traces = ts;
169 ss->zoneShift = arena->zoneShift;
170 ss->unfixedSummary = RefSetEMPTY;
171 ss->fixedSummary = RefSetEMPTY;
172 ss->arena = arena;
173 ss->wasMarked = TRUE;
174 ss->white = white;
175 STATISTIC(ss->fixRefCount = (Count)0);
176 STATISTIC(ss->segRefCount = (Count)0);
177 STATISTIC(ss->whiteSegRefCount = (Count)0);
178 STATISTIC(ss->nailCount = (Count)0);
179 STATISTIC(ss->snapCount = (Count)0);
180 STATISTIC(ss->forwardedCount = (Count)0);
181 ss->forwardedSize = (Size)0; /* see .message.data */
182 STATISTIC(ss->preservedInPlaceCount = (Count)0);
183 ss->preservedInPlaceSize = (Size)0; /* see .message.data */
184 STATISTIC(ss->copiedSize = (Size)0);
185 ss->scannedSize = (Size)0; /* see .workclock */
186 ss->sig = ScanStateSig;
187
188 AVERT(ScanState, ss);
189}
190
191
192/* ScanStateFinish -- Finish a ScanState object */
193
194void ScanStateFinish(ScanState ss)
195{
196 AVERT(ScanState, ss);
197 ss->sig = SigInvalid;
198}
199
200
201/* TraceIdCheck -- check that a TraceId is valid */
202
203Bool TraceIdCheck(TraceId ti)
204{
205 CHECKL(ti < TraceLIMIT);
206 UNUSED(ti); /* impl.c.mpm.check.unused */
207 return TRUE;
208}
209
210
211/* TraceSetCheck -- check that a TraceSet is valid */
212
213Bool TraceSetCheck(TraceSet ts)
214{
215 CHECKL(ts < (1uL << TraceLIMIT));
216 UNUSED(ts); /* impl.c.mpm.check.unused */
217 return TRUE;
218}
219
220
221/* TraceCheck -- check consistency of Trace object */
222
223Bool TraceCheck(Trace trace)
224{
225 CHECKS(Trace, trace);
226 CHECKU(Arena, trace->arena);
227 CHECKL(TraceIdCheck(trace->ti));
228 CHECKL(trace == &trace->arena->trace[trace->ti]);
229 CHECKL(TraceSetIsMember(trace->arena->busyTraces, trace));
230 CHECKL(ZoneSetSub(trace->mayMove, trace->white));
231 /* Use trace->state to check more invariants. */
232 switch(trace->state) {
233 case TraceINIT:
234 /* @@@@ What can be checked here? */
235 break;
236
237 case TraceUNFLIPPED:
238 CHECKL(!TraceSetIsMember(trace->arena->flippedTraces, trace));
239 /* @@@@ Assert that mutator is grey for trace. */
240 break;
241
242 case TraceFLIPPED:
243 CHECKL(TraceSetIsMember(trace->arena->flippedTraces, trace));
244 /* @@@@ Assert that mutator is black for trace. */
245 break;
246
247 case TraceRECLAIM:
248 CHECKL(TraceSetIsMember(trace->arena->flippedTraces, trace));
249 /* @@@@ Assert that grey set is empty for trace. */
250 break;
251
252 case TraceFINISHED:
253 CHECKL(TraceSetIsMember(trace->arena->flippedTraces, trace));
254 /* @@@@ Assert that grey and white sets is empty for trace. */
255 break;
256
257 default:
258 NOTREACHED;
259 }
260 CHECKL(BoolCheck(trace->emergency));
261 if (trace->chain != NULL)
262 CHECKU(Chain, trace->chain);
263 /* @@@@ checks for counts missing */
264 return TRUE;
265}
266
267
268/* traceUpdateCounts - dumps the counts from a ScanState into the Trace */
269
270static void traceUpdateCounts(Trace trace, ScanState ss,
271 traceAccountingPhase phase)
272{
273 switch(phase) {
274 case traceAccountingPhaseRootScan:
275 STATISTIC(trace->rootScanSize += ss->scannedSize);
276 STATISTIC(trace->rootCopiedSize += ss->copiedSize);
277 STATISTIC(++trace->rootScanCount);
278 break;
279
280 case traceAccountingPhaseSegScan:
281 trace->segScanSize += ss->scannedSize; /* see .workclock */
282 STATISTIC(trace->segCopiedSize += ss->copiedSize);
283 STATISTIC(++trace->segScanCount);
284 break;
285
286 case traceAccountingPhaseSingleScan:
287 STATISTIC(trace->singleScanSize += ss->scannedSize);
288 STATISTIC(trace->singleCopiedSize += ss->copiedSize);
289 break;
290
291 default:
292 NOTREACHED;
293 }
294 STATISTIC(trace->fixRefCount += ss->fixRefCount);
295 STATISTIC(trace->segRefCount += ss->segRefCount);
296 STATISTIC(trace->whiteSegRefCount += ss->whiteSegRefCount);
297 STATISTIC(trace->nailCount += ss->nailCount);
298 STATISTIC(trace->snapCount += ss->snapCount);
299 STATISTIC(trace->forwardedCount += ss->forwardedCount);
300 trace->forwardedSize += ss->forwardedSize; /* see .message.data */
301 STATISTIC(trace->preservedInPlaceCount += ss->preservedInPlaceCount);
302 trace->preservedInPlaceSize += ss->preservedInPlaceSize;
303
304 return;
305}
306
307
308/* traceSetUpdateCounts -- update counts for a set of traces */
309
310static void traceSetUpdateCounts(TraceSet ts, Arena arena, ScanState ss,
311 traceAccountingPhase phase)
312{
313 TraceId ti; Trace trace;
314
315 AVERT(ScanState, ss); /* check that we're not copying garbage */
316
317 TRACE_SET_ITER(ti, trace, ts, arena)
318 traceUpdateCounts(trace, ss, phase);
319 TRACE_SET_ITER_END(ti, trace, ts, arena);
320 return;
321}
322
323
324/* traceSetSignalEmergency -- move a set of traces into emergency mode. */
325
326static void traceSetSignalEmergency(TraceSet ts, Arena arena)
327{
328 TraceId ti;
329 Trace trace;
330
331 TRACE_SET_ITER(ti, trace, ts, arena)
332 trace->emergency = TRUE;
333 TRACE_SET_ITER_END(ti, trace, ts, arena);
334
335 return;
336}
337
338
339/* traceSetWhiteUnion
340 *
341 * Returns a ZoneSet describing the union of the white sets of all the
342 * specified traces. */
343
344static ZoneSet traceSetWhiteUnion(TraceSet ts, Arena arena)
345{
346 TraceId ti;
347 Trace trace;
348 ZoneSet white = ZoneSetEMPTY;
349
350 TRACE_SET_ITER(ti, trace, ts, arena)
351 white = ZoneSetUnion(white, trace->white);
352 TRACE_SET_ITER_END(ti, trace, ts, arena);
353
354 return white;
355}
356
357
358/* TraceAddWhite -- add a segment to the white set of a trace */
359
360Res TraceAddWhite(Trace trace, Seg seg)
361{
362 Res res;
363 Pool pool;
364
365 AVERT(Trace, trace);
366 AVERT(Seg, seg);
367 AVER(!TraceSetIsMember(SegWhite(seg), trace)); /* .start.black */
368
369 pool = SegPool(seg);
370 AVERT(Pool, pool);
371
372 /* Give the pool the opportunity to turn the segment white. */
373 /* If it fails, unwind. */
374 res = PoolWhiten(pool, trace, seg);
375 if (res != ResOK)
376 return res;
377
378 /* Add the segment to the approximation of the white set the */
379 /* pool made it white. */
380 if (TraceSetIsMember(SegWhite(seg), trace)) {
381 trace->white = ZoneSetUnion(trace->white, ZoneSetOfSeg(trace->arena, seg));
382 /* if the pool is a moving GC, then condemned objects may move */
383 if (pool->class->attr & AttrMOVINGGC) {
384 trace->mayMove = ZoneSetUnion(trace->mayMove,
385 ZoneSetOfSeg(trace->arena, seg));
386 }
387 }
388
389 return ResOK;
390}
391
392
393/* TraceCondemnZones -- condemn all objects in the given zones
394 *
395 * TraceCondemnZones is passed a trace in state TraceINIT, and a set of
396 * objects to condemn.
397 *
398 * @@@@ For efficiency, we ought to find the condemned set and the
399 * foundation in one search of the segment ring. This hasn't been done
400 * because some pools still use TraceAddWhite for the condemned set.
401 *
402 * @@@@ This function would be more efficient if there were a cheaper
403 * way to select the segments in a particular zone set. */
404
405Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
406{
407 Seg seg;
408 Arena arena;
409 Res res;
410
411 AVERT(Trace, trace);
412 AVER(condemnedSet != ZoneSetEMPTY);
413 AVER(trace->state == TraceINIT);
414 AVER(trace->white == ZoneSetEMPTY);
415
416 arena = trace->arena;
417
418 if (SegFirst(&seg, arena)) {
419 Addr base;
420 do {
421 base = SegBase(seg);
422 /* Segment should be black now. */
423 AVER(!TraceSetIsMember(SegGrey(seg), trace));
424 AVER(!TraceSetIsMember(SegWhite(seg), trace));
425
426 /* A segment can only be white if it is GC-able. */
427 /* This is indicated by the pool having the GC attribute */
428 /* We only condemn segments that fall entirely within */
429 /* the requested zone set. Otherwise, we would bloat the */
430 /* foundation to no gain. Note that this doesn't exclude */
431 /* any segments from which the condemned set was derived, */
432 if ((SegPool(seg)->class->attr & AttrGC) != 0
433 && ZoneSetSuper(condemnedSet, ZoneSetOfSeg(arena, seg))) {
434 res = TraceAddWhite(trace, seg);
435 if (res != ResOK)
436 return res;
437 }
438 } while (SegNext(&seg, arena, base));
439 }
440
441 /* The trace's white set must be a subset of the condemned set */
442 AVER(ZoneSetSuper(condemnedSet, trace->white));
443
444 return ResOK;
445}
446
447
448/* traceFlipBuffers -- flip all buffers in the arena */
449
450static void traceFlipBuffers(Globals arena)
451{
452 Ring nodep, nextp;
453
454 RING_FOR(nodep, &arena->poolRing, nextp) {
455 Pool pool = RING_ELT(Pool, arenaRing, nodep);
456 Ring nodeb, nextb;
457
458 AVERT(Pool, pool);
459 RING_FOR(nodeb, &pool->bufferRing, nextb) {
460 BufferFlip(RING_ELT(Buffer, poolRing, nodeb));
461 }
462 }
463}
464
465
466/* traceScanRootRes -- scan a root, with result code */
467
468static Res traceScanRootRes(TraceSet ts, Rank rank, Arena arena, Root root)
469{
470 ZoneSet white;
471 Res res;
472 ScanStateStruct ss;
473
474 white = traceSetWhiteUnion(ts, arena);
475
476 ScanStateInit(&ss, ts, arena, rank, white);
477
478 res = RootScan(&ss, root);
479
480 traceSetUpdateCounts(ts, arena, &ss, traceAccountingPhaseRootScan);
481 ScanStateFinish(&ss);
482 return res;
483}
484
485
486/* traceScanRoot
487 *
488 * Scan a root without fail. The traces may enter emergency mode to
489 * ensure this. */
490
491static void traceScanRoot(TraceSet ts, Rank rank, Arena arena, Root root)
492{
493 Res res;
494
495 res = traceScanRootRes(ts, rank, arena, root);
496 if (res != ResOK) {
497 AVER(ResIsAllocFailure(res));
498 traceSetSignalEmergency(ts, arena);
499 res = traceScanRootRes(ts, rank, arena, root);
500 /* Should be OK in emergency mode */
501 }
502 AVER(ResOK == res);
503
504 return;
505}
506
507
508/* traceFlip -- blacken the mutator */
509
510struct rootFlipClosureStruct {
511 TraceSet ts;
512 Arena arena;
513 Rank rank;
514};
515
516static Res rootFlip(Root root, void *p)
517{
518 struct rootFlipClosureStruct *rf = (struct rootFlipClosureStruct *)p;
519
520 AVERT(Root, root);
521 AVER(p != NULL);
522 AVER(TraceSetCheck(rf->ts));
523 AVERT(Arena, rf->arena);
524 AVER(RankCheck(rf->rank));
525
526 AVER(RootRank(root) <= RankEXACT); /* see .root.rank */
527
528 if (RootRank(root) == rf->rank)
529 traceScanRoot(rf->ts, rf->rank, rf->arena, root);
530
531 return ResOK;
532}
533
534static void traceFlip(Trace trace)
535{
536 Ring node, nextNode;
537 Arena arena;
538 Rank rank;
539 struct rootFlipClosureStruct rfc;
540
541 AVERT(Trace, trace);
542 rfc.ts = TraceSetSingle(trace);
543
544 arena = trace->arena;
545 rfc.arena = arena;
546 ShieldSuspend(arena);
547
548 AVER(trace->state == TraceUNFLIPPED);
549 AVER(!TraceSetIsMember(arena->flippedTraces, trace));
550
551 EVENT_PP(TraceFlipBegin, trace, arena);
552
553 traceFlipBuffers(ArenaGlobals(arena));
554
555 /* Update location dependency structures. */
556 /* mayMove is a conservative approximation of the zones of objects */
557 /* which may move during this collection. */
558 if (trace->mayMove != ZoneSetEMPTY) {
559 LDAge(arena, trace->mayMove);
560 }
561
562 /* .root.rank: At the moment we must scan all roots, because we don't have */
563 /* a mechanism for shielding them. There can't be any weak or final roots */
564 /* either, since we must protect these in order to avoid scanning them too */
565 /* early, before the pool contents. @@@@ This isn't correct if there are */
566 /* higher ranking roots than data in pools. */
567
568 for(rank = RankAMBIG; rank <= RankEXACT; ++rank) {
569 Res res;
570
571 rfc.rank = rank;
572 res = RootsIterate(ArenaGlobals(arena), rootFlip, (void *)&rfc);
573 AVER(res == ResOK);
574 }
575
576 /* .flip.alloc: Allocation needs to become black now. While we flip */
577 /* at the start, we can get away with always allocating black. This */
578 /* needs to change when we flip later (i.e. have a read-barrier */
579 /* collector), so that we allocate grey or white before the flip */
580 /* and black afterwards. For instance, see */
581 /* design.mps.poolams.invariant.alloc. */
582
583 /* Now that the mutator is black we must prevent it from reading */
584 /* grey objects so that it can't obtain white pointers. This is */
585 /* achieved by read protecting all segments containing objects */
586 /* which are grey for any of the flipped traces. */
587 for(rank = 0; rank < RankLIMIT; ++rank)
588 RING_FOR(node, ArenaGreyRing(arena, rank), nextNode) {
589 Seg seg = SegOfGreyRing(node);
590 if (TraceSetInter(SegGrey(seg), arena->flippedTraces) == TraceSetEMPTY
591 && TraceSetIsMember(SegGrey(seg), trace))
592 ShieldRaise(arena, seg, AccessREAD);
593 }
594
595 /* @@@@ When write barrier collection is implemented, this is where */
596 /* write protection should be removed for all segments which are */
597 /* no longer blacker than the mutator. Possibly this can be done */
598 /* lazily as they are touched. */
599
600 /* Mark the trace as flipped. */
601 trace->state = TraceFLIPPED;
602 arena->flippedTraces = TraceSetAdd(arena->flippedTraces, trace);
603
604 EVENT_PP(TraceFlipEnd, trace, arena);
605
606 ShieldResume(arena);
607
608 return;
609}
610
611
612/* TraceCreate -- create a Trace object
613 *
614 * Allocates and initializes a new Trace object with a TraceId which is
615 * not currently active.
616 *
617 * Returns ResLIMIT if there aren't any available trace IDs.
618 *
619 * Trace objects are allocated directly from a small array in the arena
620 * structure which is indexed by the TraceId. This is so that it's
621 * always possible to start a trace (provided there's a free TraceId)
622 * even if there's no available memory.
623 *
624 * This code is written to be adaptable to allocating Trace objects
625 * dynamically. */
626
627Res TraceCreate(Trace *traceReturn, Arena arena)
628{
629 TraceId ti;
630 Trace trace;
631
632 AVER(traceReturn != NULL);
633 AVERT(Arena, arena);
634
635 /* Find a free trace ID */
636 TRACE_SET_ITER(ti, trace, TraceSetComp(arena->busyTraces), arena)
637 goto found;
638 TRACE_SET_ITER_END(ti, trace, TraceSetComp(arena->busyTraces), arena);
639 return ResLIMIT; /* no trace IDs available */
640
641found:
642 trace = ArenaTrace(arena, ti);
643 AVER(trace->sig == SigInvalid); /* design.mps.arena.trace.invalid */
644
645 trace->arena = arena;
646 trace->white = ZoneSetEMPTY;
647 trace->mayMove = ZoneSetEMPTY;
648 trace->ti = ti;
649 trace->state = TraceINIT;
650 trace->emergency = FALSE;
651 trace->chain = NULL;
652 trace->condemned = (Size)0; /* nothing condemned yet */
653 trace->notCondemned = (Size)0;
654 trace->foundation = (Size)0; /* nothing grey yet */
655 trace->rate = (Size)0; /* no scanning to be done yet */
656 STATISTIC(trace->greySegCount = (Count)0);
657 STATISTIC(trace->greySegMax = (Count)0);
658 STATISTIC(trace->rootScanCount = (Count)0);
659 STATISTIC(trace->rootScanSize = (Size)0);
660 STATISTIC(trace->rootCopiedSize = (Size)0);
661 STATISTIC(trace->segScanCount = (Count)0);
662 trace->segScanSize = (Size)0; /* see .workclock */
663 STATISTIC(trace->segCopiedSize = (Size)0);
664 STATISTIC(trace->singleScanCount = (Count)0);
665 STATISTIC(trace->singleScanSize = (Size)0);
666 STATISTIC(trace->singleCopiedSize = (Size)0);
667 STATISTIC(trace->fixRefCount = (Count)0);
668 STATISTIC(trace->segRefCount = (Count)0);
669 STATISTIC(trace->whiteSegRefCount = (Count)0);
670 STATISTIC(trace->nailCount = (Count)0);
671 STATISTIC(trace->snapCount = (Count)0);
672 STATISTIC(trace->readBarrierHitCount = (Count)0);
673 STATISTIC(trace->pointlessScanCount = (Count)0);
674 STATISTIC(trace->forwardedCount = (Count)0);
675 trace->forwardedSize = (Size)0; /* see .message.data */
676 STATISTIC(trace->preservedInPlaceCount = (Count)0);
677 trace->preservedInPlaceSize = (Size)0; /* see .message.data */
678 STATISTIC(trace->reclaimCount = (Count)0);
679 STATISTIC(trace->reclaimSize = (Size)0);
680 trace->sig = TraceSig;
681 arena->busyTraces = TraceSetAdd(arena->busyTraces, trace);
682 AVERT(Trace, trace);
683
684 /* We suspend the mutator threads so that the PoolWhiten methods */
685 /* can calculate white sets without the mutator allocating in */
686 /* buffers under our feet. */
687 /* @@@@ This is a short-term fix for request.dylan.160098. */
688 ShieldSuspend(arena);
689
690 *traceReturn = trace;
691 return ResOK;
692}
693
694
695/* TraceDestroy -- destroy a trace object
696 *
697 * Finish and deallocate a Trace object, freeing up a TraceId.
698 *
699 * This code does not allow a Trace to be destroyed while it is active.
700 * It would be possible to allow this, but the colours of segments
701 * etc. would need to be reset to black. This also means the error
702 * paths in this file don't work. @@@@ */
703
704void TraceDestroy(Trace trace)
705{
706 AVERT(Trace, trace);
707 AVER(trace->state == TraceFINISHED);
708
709 if (trace->chain == NULL) {
710 Ring chainNode, nextChainNode;
711
712 /* Notify all the chains. */
713 RING_FOR(chainNode, &trace->arena->chainRing, nextChainNode) {
714 Chain chain = RING_ELT(Chain, chainRing, chainNode);
715
716 ChainEndGC(chain, trace);
717 }
718 } else {
719 ChainEndGC(trace->chain, trace);
720 }
721
722 STATISTIC_STAT(EVENT_PWWWWWWWWWWWW
723 (TraceStatScan, trace,
724 trace->rootScanCount, trace->rootScanSize,
725 trace->rootCopiedSize,
726 trace->segScanCount, trace->segScanSize,
727 trace->segCopiedSize,
728 trace->singleScanCount, trace->singleScanSize,
729 trace->singleCopiedSize,
730 trace->readBarrierHitCount, trace->greySegMax,
731 trace->pointlessScanCount));
732 STATISTIC_STAT(EVENT_PWWWWWWWWW
733 (TraceStatFix, trace,
734 trace->fixRefCount, trace->segRefCount,
735 trace->whiteSegRefCount,
736 trace->nailCount, trace->snapCount,
737 trace->forwardedCount, trace->forwardedSize,
738 trace->preservedInPlaceCount,
739 trace->preservedInPlaceSize));
740 STATISTIC_STAT(EVENT_PWW
741 (TraceStatReclaim, trace,
742 trace->reclaimCount, trace->reclaimSize));
743
744 trace->sig = SigInvalid;
745 trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace);
746 trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace);
747 EVENT_P(TraceDestroy, trace);
748}
749
750
751/* tracePostMessage -- post trace end message
752 *
753 * .message.data: The trace end message contains the live size
754 * (forwardedSize + preservedInPlaceSize), the condemned size
755 * (condemned), and the not-condemned size (notCondemned). */
756
757static void tracePostMessage(Trace trace)
758{
759 Arena arena;
760 void *p;
761 TraceMessage message;
762 Res res;
763
764 AVERT(Trace, trace);
765 AVER(trace->state == TraceFINISHED);
766
767 arena = trace->arena;
768 res = ControlAlloc(&p, arena, sizeof(TraceMessageStruct), FALSE);
769 if (res == ResOK) {
770 message = (TraceMessage)p;
771 TraceMessageInit(arena, message);
772 message->liveSize = trace->forwardedSize + trace->preservedInPlaceSize;
773 message->condemnedSize = trace->condemned;
774 message->notCondemnedSize = trace->notCondemned;
775 MessagePost(arena, TraceMessageMessage(message));
776 }
777
778 return;
779}
780
781
782/* traceReclaim -- reclaim the remaining objects white for this trace */
783
784static void traceReclaim(Trace trace)
785{
786 Arena arena;
787 Seg seg;
788
789 AVER(trace->state == TraceRECLAIM);
790
791 EVENT_P(TraceReclaim, trace);
792 arena = trace->arena;
793 if (SegFirst(&seg, arena)) {
794 Addr base;
795 do {
796 base = SegBase(seg);
797 /* There shouldn't be any grey stuff left for this trace. */
798 AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace));
799
800 if (TraceSetIsMember(SegWhite(seg), trace)) {
801 AVER_CRITICAL((SegPool(seg)->class->attr & AttrGC) != 0);
802 STATISTIC(++trace->reclaimCount);
803 PoolReclaim(SegPool(seg), trace, seg);
804
805 /* If the segment still exists, it should no longer be white. */
806 /* Note that the seg returned by this SegOfAddr may not be */
807 /* the same as the one above, but in that case it's new and */
808 /* still shouldn't be white for this trace. */
809
810 /* The code from the class-specific reclaim methods to */
811 /* unwhiten the segment could in fact be moved here. */
812 {
813 Seg nonWhiteSeg = NULL; /* prevents compiler warning */
814 AVER_CRITICAL(!(SegOfAddr(&nonWhiteSeg, arena, base)
815 && TraceSetIsMember(SegWhite(nonWhiteSeg), trace)));
816 UNUSED(nonWhiteSeg); /* impl.c.mpm.check.unused */
817 }
818 }
819 } while (SegNext(&seg, arena, base));
820 }
821
822 trace->state = TraceFINISHED;
823 tracePostMessage(trace);
824 return;
825}
826
827
828/* traceFindGrey -- find a grey segment
829 *
830 * This function finds a segment which is grey for the trace given and
831 * which does not have a higher rank than any other such segment (i.e.,
832 * a next segment to scan). */
833
834static Bool traceFindGrey(Seg *segReturn, Rank *rankReturn,
835 Arena arena, TraceId ti)
836{
837 Rank rank;
838 Trace trace;
839 Ring node, nextNode;
840
841 AVER(segReturn != NULL);
842 AVER(TraceIdCheck(ti));
843
844 trace = ArenaTrace(arena, ti);
845
846 for(rank = 0; rank < RankLIMIT; ++rank) {
847 RING_FOR(node, ArenaGreyRing(arena, rank), nextNode) {
848 Seg seg = SegOfGreyRing(node);
849 AVERT(Seg, seg);
850 AVER(SegGrey(seg) != TraceSetEMPTY);
851 AVER(RankSetIsMember(SegRankSet(seg), rank));
852 if (TraceSetIsMember(SegGrey(seg), trace)) {
853 *segReturn = seg; *rankReturn = rank;
854 return TRUE;
855 }
856 }
857 }
858
859 return FALSE; /* There are no grey segments for this trace. */
860}
861
862
863/* ScanStateSetSummary -- set the summary of scanned references
864 *
865 * This function sets unfixedSummary and fixedSummary such that
866 * ScanStateSummary will return the summary passed. Subsequently fixed
867 * references are accumulated into this result. */
868
869void ScanStateSetSummary(ScanState ss, RefSet summary)
870{
871 AVERT(ScanState, ss);
872 /* Can't check summary, as it can be anything. */
873
874 ss->unfixedSummary = RefSetEMPTY;
875 ss->fixedSummary = summary;
876 AVER(ScanStateSummary(ss) == summary);
877}
878
879
880/* ScanStateSummary -- calculate the summary of scanned references
881 *
882 * The summary of the scanned references is the summary of the unfixed
883 * references, minus the white set, plus the summary of the fixed
884 * references. This is because TraceFix is called for all references in
885 * the white set, and accumulates a summary of references after they
886 * have been fixed. */
887
888RefSet ScanStateSummary(ScanState ss)
889{
890 AVERT(ScanState, ss);
891
892 return RefSetUnion(ss->fixedSummary,
893 RefSetDiff(ss->unfixedSummary, ss->white));
894}
895
896
897/* traceScanSegRes -- scan a segment to remove greyness
898 *
899 * @@@@ During scanning, the segment should be write-shielded to prevent
900 * any other threads from updating it while fix is being applied to it
901 * (because fix is not atomic). At the moment, we don't bother, because
902 * we know that all threads are suspended. */
903
904static Res traceScanSegRes(TraceSet ts, Rank rank, Arena arena, Seg seg)
905{
906 Bool wasTotal;
907 ZoneSet white;
908 Res res;
909
910 /* The reason for scanning a segment is that it's grey. */
911 AVER(TraceSetInter(ts, SegGrey(seg)) != TraceSetEMPTY);
912 EVENT_UUPP(TraceScanSeg, ts, rank, arena, seg);
913
914 white = traceSetWhiteUnion(ts, arena);
915
916 /* only scan a segment if it refers to the white set */
917 if (ZoneSetInter(white, SegSummary(seg)) == ZoneSetEMPTY) {
918 PoolBlacken(SegPool(seg), ts, seg);
919 /* setup result code to return later */
920 res = ResOK;
921 } else { /* scan it */
922 ScanStateStruct ss;
923 ScanStateInit(&ss, ts, arena, rank, white);
924
925 /* Expose the segment to make sure we can scan it. */
926 ShieldExpose(arena, seg);
927 res = PoolScan(&wasTotal, &ss, SegPool(seg), seg);
928 /* Cover, regardless of result */
929 ShieldCover(arena, seg);
930
931 traceSetUpdateCounts(ts, arena, &ss, traceAccountingPhaseSegScan);
932 /* Count segments scanned pointlessly */
933 STATISTIC_STAT
934 ({
935 TraceId ti; Trace trace;
936 Count whiteSegRefCount = 0;
937
938 TRACE_SET_ITER(ti, trace, ts, arena)
939 whiteSegRefCount += trace->whiteSegRefCount;
940 TRACE_SET_ITER_END(ti, trace, ts, arena);
941 if (whiteSegRefCount == 0)
942 TRACE_SET_ITER(ti, trace, ts, arena)
943 ++trace->pointlessScanCount;
944 TRACE_SET_ITER_END(ti, trace, ts, arena);
945 });
946
947 /* following is true whether or not scan was total */
948 /* See design.mps.scan.summary.subset. */
949 AVER(RefSetSub(ss.unfixedSummary, SegSummary(seg)));
950
951 if (res != ResOK || !wasTotal) {
952 /* scan was partial, so... */
953 /* scanned summary should be ORed into segment summary. */
954 SegSetSummary(seg, RefSetUnion(SegSummary(seg), ScanStateSummary(&ss)));
955 } else {
956 /* all objects on segment have been scanned, so... */
957 /* scanned summary should replace the segment summary. */
958 SegSetSummary(seg, ScanStateSummary(&ss));
959 }
960
961 ScanStateFinish(&ss);
962 }
963
964 if (res == ResOK) {
965 /* The segment is now black only if scan was successful. */
966 /* Remove the greyness from it. */
967 SegSetGrey(seg, TraceSetDiff(SegGrey(seg), ts));
968 }
969
970 return res;
971}
972
973
974/* traceScanSeg
975 *
976 * Scans a segment without fail. May put the traces into emergency mode
977 * to ensure this. */
978
979static void traceScanSeg(TraceSet ts, Rank rank, Arena arena, Seg seg)
980{
981 Res res;
982
983 res = traceScanSegRes(ts, rank, arena, seg);
984 if (res != ResOK) {
985 AVER(ResIsAllocFailure(res));
986 traceSetSignalEmergency(ts, arena);
987 res = traceScanSegRes(ts, rank, arena, seg);
988 /* should be OK in emergency mode */
989 }
990 AVER(ResOK == res);
991
992 return;
993}
994
995
996/* TraceSegAccess -- handle barrier hit on a segment */
997
998void TraceSegAccess(Arena arena, Seg seg, AccessSet mode)
999{
1000 TraceId ti;
1001
1002 AVERT(Arena, arena);
1003 AVERT(Seg, seg);
1004
1005 /* If it's a read access, then the segment must be grey for a trace */
1006 /* which is flipped. */
1007 AVER((mode & SegSM(seg) & AccessREAD) == 0
1008 || TraceSetInter(SegGrey(seg), arena->flippedTraces) != TraceSetEMPTY);
1009
1010 /* If it's a write acess, then the segment must have a summary that */
1011 /* is smaller than the mutator's summary (which is assumed to be */
1012 /* RefSetUNIV). */
1013 AVER((mode & SegSM(seg) & AccessWRITE) == 0 || SegSummary(seg) != RefSetUNIV);
1014
1015 EVENT_PPU(TraceAccess, arena, seg, mode);
1016
1017 if ((mode & SegSM(seg) & AccessREAD) != 0) { /* read barrier? */
1018 /* Pick set of traces to scan for: */
1019 TraceSet traces = arena->flippedTraces;
1020
1021 /* .scan.conservative: At the moment we scan at RankEXACT. Really */
1022 /* we should be scanning at the "phase" of the trace, which is the */
1023 /* minimum rank of all grey segments. (see request.mps.170160) */
1024 traceScanSeg(traces, RankEXACT, arena, seg);
1025
1026 /* The pool should've done the job of removing the greyness that */
1027 /* was causing the segment to be protected, so that the mutator */
1028 /* can go ahead and access it. */
1029 AVER(TraceSetInter(SegGrey(seg), traces) == TraceSetEMPTY);
1030
1031 STATISTIC_STAT({
1032 Trace trace;
1033
1034 TRACE_SET_ITER(ti, trace, traces, arena)
1035 ++trace->readBarrierHitCount;
1036 TRACE_SET_ITER_END(ti, trace, traces, arena);
1037 });
1038 } else { /* write barrier */
1039 STATISTIC(++arena->writeBarrierHitCount);
1040 }
1041
1042 /* The write barrier handling must come after the read barrier, */
1043 /* because the latter may set the summary and raise the write barrier. */
1044 if ((mode & SegSM(seg) & AccessWRITE) != 0) /* write barrier? */
1045 SegSetSummary(seg, RefSetUNIV);
1046
1047 /* The segment must now be accessible. */
1048 AVER((mode & SegSM(seg)) == AccessSetEMPTY);
1049}
1050
1051
1052/* TraceFix -- fix a reference */
1053
1054Res TraceFix(ScanState ss, Ref *refIO)
1055{
1056 Ref ref;
1057 Tract tract;
1058 Pool pool;
1059
1060 /* See design.mps.trace.fix.noaver */
1061 AVERT_CRITICAL(ScanState, ss);
1062 AVER_CRITICAL(refIO != NULL);
1063
1064 ref = *refIO;
1065
1066 STATISTIC(++ss->fixRefCount);
1067 EVENT_PPAU(TraceFix, ss, refIO, ref, ss->rank);
1068
1069 TRACT_OF_ADDR(&tract, ss->arena, ref);
1070 if (tract) {
1071 if (TraceSetInter(TractWhite(tract), ss->traces) != TraceSetEMPTY) {
1072 Seg seg;
1073 if (TRACT_SEG(&seg, tract)) {
1074 Res res;
1075 STATISTIC(++ss->segRefCount);
1076 STATISTIC(++ss->whiteSegRefCount);
1077 EVENT_P(TraceFixSeg, seg);
1078 EVENT_0(TraceFixWhite);
1079 pool = TractPool(tract);
1080 /* Could move the rank switch here from the class-specific */
1081 /* fix methods. */
1082 res = PoolFix(pool, ss, seg, refIO);
1083 if (res != ResOK)
1084 return res;
1085 }
1086 } else {
1087 /* Tract isn't white. Don't compute seg for non-statistical */
1088 /* variety. See design.mps.trace.fix.tractofaddr */
1089 STATISTIC_STAT
1090 ({
1091 Seg seg;
1092 if (TRACT_SEG(&seg, tract)) {
1093 ++ss->segRefCount;
1094 EVENT_P(TraceFixSeg, seg);
1095 }
1096 });
1097 }
1098 } else {
1099 /* See design.mps.trace.exact.legal */
1100 AVER(ss->rank < RankEXACT
1101 || !ArenaIsReservedAddr(ss->arena, ref));
1102 }
1103
1104 /* See design.mps.trace.fix.fixed.all */
1105 ss->fixedSummary = RefSetAdd(ss->arena, ss->fixedSummary, *refIO);
1106
1107 return ResOK;
1108}
1109
1110
1111/* TraceFixEmergency -- fix a reference in emergency mode */
1112
1113Res TraceFixEmergency(ScanState ss, Ref *refIO)
1114{
1115 Ref ref;
1116 Tract tract;
1117 Pool pool;
1118
1119 AVERT(ScanState, ss);
1120 AVER(refIO != NULL);
1121
1122 ref = *refIO;
1123
1124 STATISTIC(++ss->fixRefCount);
1125 EVENT_PPAU(TraceFix, ss, refIO, ref, ss->rank);
1126
1127 TRACT_OF_ADDR(&tract, ss->arena, ref);
1128 if (tract) {
1129 if (TraceSetInter(TractWhite(tract), ss->traces) != TraceSetEMPTY) {
1130 Seg seg;
1131 if (TRACT_SEG(&seg, tract)) {
1132 STATISTIC(++ss->segRefCount);
1133 STATISTIC(++ss->whiteSegRefCount);
1134 EVENT_P(TraceFixSeg, seg);
1135 EVENT_0(TraceFixWhite);
1136 pool = TractPool(tract);
1137 PoolFixEmergency(pool, ss, seg, refIO);
1138 }
1139 } else {
1140 /* Tract isn't white. Don't compute seg for non-statistical */
1141 /* variety. See design.mps.trace.fix.tractofaddr */
1142 STATISTIC_STAT
1143 ({
1144 Seg seg;
1145 if (TRACT_SEG(&seg, tract)) {
1146 ++ss->segRefCount;
1147 EVENT_P(TraceFixSeg, seg);
1148 }
1149 });
1150 }
1151 } else {
1152 /* See design.mps.trace.exact.legal */
1153 AVER(ss->rank < RankEXACT ||
1154 !ArenaIsReservedAddr(ss->arena, ref));
1155 }
1156
1157 /* See design.mps.trace.fix.fixed.all */
1158 ss->fixedSummary = RefSetAdd(ss->arena, ss->fixedSummary, *refIO);
1159
1160 return ResOK;
1161}
1162
1163
1164/* traceScanSingleRefRes -- scan a single reference, with result code */
1165
1166static Res traceScanSingleRefRes(TraceSet ts, Rank rank, Arena arena,
1167 Seg seg, Ref *refIO)
1168{
1169 RefSet summary;
1170 ZoneSet white;
1171 Res res;
1172 ScanStateStruct ss;
1173
1174 EVENT_UUPA(TraceScanSingleRef, ts, rank, arena, (Addr)refIO);
1175
1176 white = traceSetWhiteUnion(ts, arena);
1177 if (ZoneSetInter(SegSummary(seg), white) == ZoneSetEMPTY) {
1178 return ResOK;
1179 }
1180
1181 ScanStateInit(&ss, ts, arena, rank, white);
1182 ShieldExpose(arena, seg);
1183
1184 TRACE_SCAN_BEGIN(&ss) {
1185 res = TRACE_FIX(&ss, refIO);
1186 } TRACE_SCAN_END(&ss);
1187 ss.scannedSize = sizeof *refIO;
1188
1189 summary = SegSummary(seg);
1190 summary = RefSetAdd(arena, summary, *refIO);
1191 SegSetSummary(seg, summary);
1192 ShieldCover(arena, seg);
1193
1194 traceSetUpdateCounts(ts, arena, &ss, traceAccountingPhaseSingleScan);
1195 ScanStateFinish(&ss);
1196
1197 return res;
1198}
1199
1200
1201/* TraceScanSingleRef -- scan a single reference
1202 *
1203 * This one can't fail. It may put the traces into emergency mode in
1204 * order to achieve this. */
1205
1206void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena,
1207 Seg seg, Ref *refIO)
1208{
1209 Res res;
1210
1211 AVER(TraceSetCheck(ts));
1212 AVER(RankCheck(rank));
1213 AVERT(Arena, arena);
1214 AVER(SegCheck(seg));
1215 AVER(refIO != NULL);
1216
1217 res = traceScanSingleRefRes(ts, rank, arena, seg, refIO);
1218 if (res != ResOK) {
1219 traceSetSignalEmergency(ts, arena);
1220 res = traceScanSingleRefRes(ts, rank, arena, seg, refIO);
1221 /* ought to be OK in emergency mode now */
1222 }
1223 AVER(ResOK == res);
1224
1225 return;
1226}
1227
1228
1229/* TraceScanArea -- scan contiguous area of references
1230 *
1231 * This is a convenience function for scanning the contiguous area
1232 * [base, limit). I.e., it calls Fix on all words from base up to
1233 * limit, inclusive of base and exclusive of limit. */
1234
1235Res TraceScanArea(ScanState ss, Addr *base, Addr *limit)
1236{
1237 Res res;
1238 Addr *p;
1239 Ref ref;
1240
1241 AVER(base != NULL);
1242 AVER(limit != NULL);
1243 AVER(base < limit);
1244
1245 EVENT_PPP(TraceScanArea, ss, base, limit);
1246
1247 TRACE_SCAN_BEGIN(ss) {
1248 p = base;
1249 loop:
1250 if (p >= limit) goto out;
1251 ref = *p++;
1252 if (!TRACE_FIX1(ss, ref))
1253 goto loop;
1254 res = TRACE_FIX2(ss, p-1);
1255 if (res == ResOK)
1256 goto loop;
1257 return res;
1258 out:
1259 AVER(p == limit);
1260 } TRACE_SCAN_END(ss);
1261
1262 return ResOK;
1263}
1264
1265
1266/* TraceScanAreaTagged -- scan contiguous area of tagged references
1267 *
1268 * This is as TraceScanArea except words are only fixed if they are
1269 * tagged as Dylan references (i.e., bottom two bits are zero). @@@@
1270 * This Dylan-specificness should be generalized in some way. */
1271
1272Res TraceScanAreaTagged(ScanState ss, Addr *base, Addr *limit)
1273{
1274 return TraceScanAreaMasked(ss, base, limit, (Word)3);
1275}
1276
1277
1278/* TraceScanAreaMasked -- scan contiguous area of filtered references
1279 *
1280 * This is as TraceScanArea except words are only fixed if they are zero
1281 * when masked with a mask. */
1282
1283Res TraceScanAreaMasked(ScanState ss, Addr *base, Addr *limit, Word mask)
1284{
1285 Res res;
1286 Addr *p;
1287 Ref ref;
1288
1289 AVER(base != NULL);
1290 AVER(limit != NULL);
1291 AVER(base < limit);
1292
1293 EVENT_PPP(TraceScanAreaTagged, ss, base, limit);
1294
1295 TRACE_SCAN_BEGIN(ss) {
1296 p = base;
1297 loop:
1298 if (p >= limit) goto out;
1299 ref = *p++;
1300 if (((Word)ref & mask) != 0) goto loop;
1301 if (!TRACE_FIX1(ss, ref)) goto loop;
1302 res = TRACE_FIX2(ss, p-1);
1303 if (res == ResOK)
1304 goto loop;
1305 return res;
1306 out:
1307 AVER(p == limit);
1308 } TRACE_SCAN_END(ss);
1309
1310 return ResOK;
1311}
1312
1313
1314/* traceCondemnAll -- condemn everything and notify all the chains */
1315
1316static Res traceCondemnAll(Trace trace)
1317{
1318 Res res;
1319 Arena arena;
1320 Ring chainNode, nextChainNode;
1321 Bool haveWhiteSegs = FALSE;
1322
1323 arena = trace->arena;
1324 AVERT(Arena, arena);
1325 /* Condemn all the chains. */
1326 RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
1327 Chain chain = RING_ELT(Chain, chainRing, chainNode);
1328
1329 AVERT(Chain, chain);
1330 res = ChainCondemnAll(chain, trace);
1331 if (res != ResOK)
1332 goto failBegin;
1333 haveWhiteSegs = TRUE;
1334 }
1335 /* Notify all the chains. */
1336 RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
1337 Chain chain = RING_ELT(Chain, chainRing, chainNode);
1338
1339 ChainStartGC(chain, trace);
1340 }
1341 return ResOK;
1342
1343failBegin:
1344 AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */
1345 return res;
1346}
1347
1348
1349/* Collection control parameters */
1350
1351double TraceTopGenMortality = 0.51;
1352double TraceWorkFactor = 0.25;
1353
1354
1355/* TraceStart -- condemn a set of objects and start collection
1356 *
1357 * TraceStart should be passed a trace with state TraceINIT, i.e.,
1358 * recently returned from TraceCreate, with some condemned segments
1359 * added. mortality is the fraction of the condemned set expected to
1360 * survive. finishingTime is relative to the current polling clock, see
1361 * design.mps.arena.poll.clock.
1362 *
1363 * .start.black: All segments are black w.r.t. a newly allocated trace.
1364 * However, if TraceStart initialized segments to black when it
1365 * calculated the grey set then this condition could be relaxed, making
1366 * it easy to destroy traces half-way through. */
1367
1368static Res rootGrey(Root root, void *p)
1369{
1370 Trace trace = (Trace)p;
1371
1372 AVERT(Root, root);
1373 AVERT(Trace, trace);
1374
1375 if (ZoneSetInter(RootSummary(root), trace->white) != ZoneSetEMPTY)
1376 RootGrey(root, trace);
1377
1378 return ResOK;
1379}
1380
1381void TraceStart(Trace trace, double mortality, double finishingTime)
1382{
1383 Arena arena;
1384 Seg seg;
1385 Size size;
1386 Res res;
1387
1388 AVERT(Trace, trace);
1389 AVER(trace->state == TraceINIT);
1390 AVER(0.0 <= mortality && mortality <= 1.0);
1391 arena = trace->arena;
1392 AVER(finishingTime >= 0.0);
1393
1394 /* From the already set up white set, derive a grey set. */
1395
1396 /* @@@@ Instead of iterating over all the segments, we could */
1397 /* iterate over all pools which are scannable and thence over */
1398 /* all their segments. This might be better if the minority */
1399 /* of segments are scannable. Perhaps we should choose */
1400 /* dynamically which method to use. */
1401
1402 if (SegFirst(&seg, arena)) {
1403 Addr base;
1404 do {
1405 base = SegBase(seg);
1406 size = SegSize(seg);
1407 AVER(!TraceSetIsMember(SegGrey(seg), trace));
1408
1409 /* A segment can only be grey if it contains some references. */
1410 /* This is indicated by the rankSet begin non-empty. Such */
1411 /* segments may only belong to scannable pools. */
1412 if (SegRankSet(seg) != RankSetEMPTY) {
1413 /* Segments with ranks may only belong to scannable pools. */
1414 AVER((SegPool(seg)->class->attr & AttrSCAN) != 0);
1415
1416 /* Turn the segment grey if there might be a reference in it */
1417 /* to the white set. This is done by seeing if the summary */
1418 /* of references in the segment intersects with the */
1419 /* approximation to the white set. */
1420 if (ZoneSetInter(SegSummary(seg), trace->white) != ZoneSetEMPTY) {
1421 PoolGrey(SegPool(seg), trace, seg);
1422 if (TraceSetIsMember(SegGrey(seg), trace))
1423 trace->foundation += size;
1424 }
1425
1426 if ((SegPool(seg)->class->attr & AttrGC)
1427 && !TraceSetIsMember(SegWhite(seg), trace))
1428 trace->notCondemned += size;
1429 }
1430 } while (SegNext(&seg, arena, base));
1431 }
1432
1433 res = RootsIterate(ArenaGlobals(arena), rootGrey, (void *)trace);
1434 AVER(res == ResOK);
1435
1436 STATISTIC_STAT(EVENT_PW(ArenaWriteFaults, arena, arena->writeBarrierHitCount));
1437
1438 /* Calculate the rate of scanning. */
1439 {
1440 Size sSurvivors = (Size)(trace->condemned * (1.0 - mortality));
1441 double nPolls = finishingTime / ArenaPollALLOCTIME;
1442
1443 /* There must be at least one poll. */
1444 if (nPolls < 1.0)
1445 nPolls = 1.0;
1446 /* We use casting to long to truncate nPolls down to the nearest */
1447 /* integer, so try to make sure it fits. */
1448 if (nPolls >= (double)LONG_MAX)
1449 nPolls = (double)LONG_MAX;
1450 /* rate equals scanning work per number of polls available */
1451 trace->rate = (trace->foundation + sSurvivors) / (long)nPolls + 1;
1452 }
1453
1454 STATISTIC_STAT(EVENT_PWWWWDD(TraceStatCondemn, trace,
1455 trace->condemned, trace->notCondemned,
1456 trace->foundation, trace->rate,
1457 mortality, finishingTime));
1458 trace->state = TraceUNFLIPPED;
1459
1460 /* All traces must flip at beginning at the moment. */
1461 traceFlip(trace);
1462
1463 return;
1464}
1465
1466
1467/* traceWorkClock -- a measure of the work done for this trace
1468 *
1469 * .workclock: Segment scanning work is the regulator. */
1470
1471#define traceWorkClock(trace) (trace)->segScanSize
1472
1473
1474/* traceQuantum -- progresses a trace by one quantum */
1475
1476static void traceQuantum(Trace trace)
1477{
1478 Size pollEnd;
1479
1480 pollEnd = traceWorkClock(trace) + trace->rate;
1481 do {
1482 switch(trace->state) {
1483 case TraceUNFLIPPED:
1484 /* all traces are flipped in TraceStart at the moment */
1485 NOTREACHED;
1486 break;
1487 case TraceFLIPPED: {
1488 Arena arena = trace->arena;
1489 Seg seg;
1490 Rank rank;
1491
1492 if (traceFindGrey(&seg, &rank, arena, trace->ti)) {
1493 AVER((SegPool(seg)->class->attr & AttrSCAN) != 0);
1494 traceScanSeg(TraceSetSingle(trace), rank, arena, seg);
1495 } else
1496 trace->state = TraceRECLAIM;
1497 } break;
1498 case TraceRECLAIM:
1499 traceReclaim(trace);
1500 break;
1501 default:
1502 NOTREACHED;
1503 break;
1504 }
1505 } while (trace->state != TraceFINISHED
1506 && (trace->emergency || traceWorkClock(trace) < pollEnd));
1507}
1508
1509
1510/* TracePoll -- Check if there's any tracing work to be done */
1511
1512void TracePoll(Globals globals)
1513{
1514 Trace trace;
1515 Res res;
1516 Arena arena;
1517
1518 AVERT(Globals, globals);
1519 arena = GlobalsArena(globals);
1520
1521 if (arena->busyTraces == TraceSetEMPTY) {
1522 /* If no traces are going on, see if we need to start one. */
1523 Size sFoundation, sCondemned, sSurvivors, sConsTrace;
1524 double tTracePerScan; /* tTrace/cScan */
1525 double dynamicDeferral;
1526
1527 /* Compute dynamic criterion. See strategy.lisp-machine. */
1528 AVER(TraceTopGenMortality >= 0.0);
1529 AVER(TraceTopGenMortality <= 1.0);
1530 sFoundation = (Size)0; /* condemning everything, only roots @@@@ */
1531 /* @@@@ sCondemned should be scannable only */
1532 sCondemned = ArenaCommitted(arena) - ArenaSpareCommitted(arena);
1533 sSurvivors = (Size)(sCondemned * (1 - TraceTopGenMortality));
1534 tTracePerScan = sFoundation + (sSurvivors * (1 + TraceCopyScanRATIO));
1535 AVER(TraceWorkFactor >= 0);
1536 AVER(sSurvivors + tTracePerScan * TraceWorkFactor <= (double)SizeMAX);
1537 sConsTrace = (Size)(sSurvivors + tTracePerScan * TraceWorkFactor);
1538 dynamicDeferral = (double)sConsTrace - (double)ArenaAvail(arena);
1539
1540 if (dynamicDeferral > 0.0) { /* start full GC */
1541 double finishingTime;
1542
1543 res = TraceCreate(&trace, arena);
1544 AVER(res == ResOK); /* succeeds because no other trace is busy */
1545 traceCondemnAll(trace);
1546 finishingTime = ArenaAvail(arena)
1547 - trace->condemned * (1.0 - TraceTopGenMortality);
1548 if (finishingTime < 0)
1549 /* Run out of time, should really try a smaller collection. @@@@ */
1550 finishingTime = 0.0;
1551 TraceStart(trace, TraceTopGenMortality, finishingTime);
1552 } else { /* Find the nursery most over its capacity. */
1553 Ring node, nextNode;
1554 double firstTime = 0.0;
1555 Chain firstChain = NULL;
1556
1557 RING_FOR(node, &arena->chainRing, nextNode) {
1558 Chain chain = RING_ELT(Chain, chainRing, node);
1559 double time;
1560
1561 AVERT(Chain, chain);
1562 time = ChainDeferral(chain);
1563 if (time < firstTime) {
1564 firstTime = time; firstChain = chain;
1565 }
1566 }
1567
1568 /* If one was found, start collection on that chain. */
1569 if (firstTime < 0) {
1570 double mortality;
1571
1572 res = TraceCreate(&trace, arena);
1573 AVER(res == ResOK);
1574 res = ChainCondemnAuto(&mortality, firstChain, trace);
1575 if (res != ResOK)
1576 goto failCondemn;
1577 trace->chain = firstChain;
1578 ChainStartGC(firstChain, trace);
1579 TraceStart(trace, mortality, trace->condemned * TraceWorkFactor);
1580 }
1581 } /* (dynamicDeferral > 0.0) */
1582 } /* (arena->busyTraces == TraceSetEMPTY) */
1583
1584 /* If there is a trace, do one quantum of work. */
1585 if (arena->busyTraces != TraceSetEMPTY) {
1586 trace = ArenaTrace(arena, (TraceId)0);
1587 AVER(arena->busyTraces == TraceSetSingle(trace));
1588 traceQuantum(trace);
1589 if (trace->state == TraceFINISHED)
1590 TraceDestroy(trace);
1591 }
1592 return;
1593
1594failCondemn:
1595 TraceDestroy(trace);
1596}
1597
1598
1599/* ArenaClamp -- clamp the arena (no new collections) */
1600
1601void ArenaClamp(Globals globals)
1602{
1603 AVERT(Globals, globals);
1604 globals->clamped = TRUE;
1605}
1606
1607
1608/* ArenaRelease -- release the arena (allow new collections) */
1609
1610void ArenaRelease(Globals globals)
1611{
1612 AVERT(Globals, globals);
1613 globals->clamped = FALSE;
1614 TracePoll(globals);
1615}
1616
1617
1618/* ArenaClamp -- finish all collections and clamp the arena */
1619
1620void ArenaPark(Globals globals)
1621{
1622 TraceId ti;
1623 Trace trace;
1624 Arena arena;
1625
1626 AVERT(Globals, globals);
1627 arena = GlobalsArena(globals);
1628
1629 globals->clamped = TRUE;
1630
1631 while (arena->busyTraces != TraceSetEMPTY) {
1632 /* Poll active traces to make progress. */
1633 TRACE_SET_ITER(ti, trace, arena->busyTraces, arena)
1634 traceQuantum(trace);
1635 if (trace->state == TraceFINISHED)
1636 TraceDestroy(trace);
1637 TRACE_SET_ITER_END(ti, trace, arena->busyTraces, arena);
1638 }
1639}
1640
1641
1642/* ArenaCollect -- collect everything in arena */
1643
1644Res ArenaCollect(Globals globals)
1645{
1646 Trace trace;
1647 Res res;
1648
1649 AVERT(Globals, globals);
1650 ArenaPark(globals);
1651
1652 res = TraceCreate(&trace, GlobalsArena(globals));
1653 AVER(res == ResOK); /* should be a trace available -- we're parked */
1654
1655 res = traceCondemnAll(trace);
1656 if (res != ResOK)
1657 goto failBegin;
1658
1659 TraceStart(trace, 0.0, 0.0);
1660 ArenaPark(globals);
1661 return ResOK;
1662
1663failBegin:
1664 TraceDestroy(trace);
1665 return res;
1666}