This wiki article contains incomplete and informal notes about the MPS, the precursor to more formal documentation. Not confidential. Readership: MPS users and developers.

Introduction

Tracing is when the MPS follows the dag of references to find all reachable objects.

Setting up a trace

MPS Trace setup.  Original drawing: trace.graffle, derived from //info.ravenbrook.com/project/mps/master/code/trace.c#18, 2007-01-08

Scan: advance a trace

The unit of trace scanning is the segment. Trace calls PoolScan(&wasTotal, &ss, SegPool(seg), seg). PoolScan must make the seg non-grey, or return a failure code.

.scan.change:

When scan encounters a ref, the ref and the referent object get fixed. Fixing can cause several kinds of change; these changes happen immediately, right in the middle of scanning. WARNING: These changes can be on the same segment we are scanning:

Scanning code must behave correctly when these changes affect the segment currently being scanned. The tricky issues are: not crashing, completeness of de-greying, getting the final summary correct, doing the accounting correctly, etc.

.scan.loop:

Looping is all about completeness of de-greying; it is affected by both colour and buffers.

.scan.loop.once: If the seg is full and non-white (eg. all grey) when scanning starts, then a single linear scan will make the seg non-grey (in fact: all black). In this case only, we do not need a loop.

Otherwise, there are two ways new grey objects can appear in the segment while we are scanning it:

.scan.loop.white: If the seg contains some white objects when we start a linear scan, then a Ref that points backwards into the same seg can fix and grey an object that was skipped as white when the linear scan passed over it. So scanning must loop until it the whole seg is non-grey. (This condition is a case of .scan.change.obj-colour).

.scan.loop.buffer: If the seg has, or could get, a Forwarding-grey (or Mutator-grey, not yet implemented) buffer, then new grey objects can arrive during the scan. WARNING: buffers move around during scanning: a buffer could be attached, or detached, several times during scanning a single segment, especially in low memory situations. (This condition is a case of .scan.change.buffer).

.scan.loop.terminate: There needs to be communication between the thing that makes new grey objects, and the scan loop termination condition.

.scan.loop.terminate.no-new-marks: The usual termination condition for .scan.loop.white is that the last iteration produced no new marks. A newMarks boolean is used by AMC and AMS. (Perhaps it is also needed for other reasons?). [Note: It might be reasonable to make buffers touch the same newMarks boolean, to provide the termination condition for .scan.loop.buffer. RHSK 2007-01-18].

[.scan.loop.terminate.seggrey: An alternative would be for newly fixed or forwarded objects to do SegSetGrey to trigger another iteration. They have to do this anyway, for the case that the preserved object is on a different seg. To use this to control scan loop termination, scan would have to de-grey the seg at scan start, which is slightly alarming. But I can't think of any actual problem with it, except that the mutator musn't see the seg yet, which mucks up the shield abstraction. Hmmm. It still might be better than lots of special-case code in pools though? RHSK 2007-01-18]

.scan.loop.de-grey: After a resOK scan, the seg is set non-grey by traceScanSegRes() in trace.c.

.scan.loop.no-escape: So there is no way to 'escape from' the complete-degrey requirement, and no way to mark this seg as 'still grey' at the end of the scan, except by returning an error.

.scan.loop.emergency: Any error returned by scan (which must be in the ResIsAllocFailure category) will put the traces into emergency mode and cause an immediate re-scan.

.scan.total:

Total versus non-Total scans is all about updating the segment summary; it is affected by both colour and buffers.

The current design is that the segment summary is not updated as new refs (from relocating fixes or splats) are written into the seg during the scan. Instead, the summary of the old and new refs seen by the scan is kept in the scan state: ss->unfixedSummary and ss->fixedSummary.

.scan.total.not: A scan does not necessarily see all objects: objects that were already black at scan start are usually skipped; and objects that remain white at scan end are always skipped. This is the !wasTotal case, and the fixedSummary resulting from the scan must be unioned into the segment summary.

.scan.total.yes: But if all objects in the segment were scanned*, PoolScan sets wasTotal to true. (*: for example, if the whole seg was grey at scan start). The trace then uses ss->fixedSummary to replace the old segment summary.

Scan details

This is how a non-trivial scan of a seg happens in AMC:

  traceScanSeg  /* without fail: if needed triggers emergency retry (cannot fail) */
    traceScanSegRes
      ScanStateInit
        ss->fix = TraceFix, or TraceFixEmergency
      ShieldExpose
      PoolScan
        (*pool->class->scan)
          AMCScan
            while (buffer still leaving trail)
              (*pool->format->scan)(ss, base, limit);
                [for example]
                dylan_scan
                  mps_fix
                  MPS_FIX12
                    ss->unfixedSummary += ref
                    (*ss->fix)(ss, ref_io)
                      TraceFix
                        TraceSetInter(TractWhite(tract(ref)), ss->traces)?
                          TRACT_SEG?
                            PoolFix(pool, ss, seg, refIO)
                            = (*pool->fix)(pool, ss, seg, refIO)
                              ...see below...
                    ss->fixedSummary += newRef
      ShieldCover

Where:

PoolFix(pool, ss, seg, refIO)
= (*pool->fix)(pool, ss, seg, refIO)
  AMCFix, AMCHeaderFix
    ShieldExpose(ref)
    (*pool->format->isMoved)(ref)?
      no:
        /* Forward */
        newRef = BUFFER_RESERVE(seg->gen->forward)
        ShieldExpose(newRef)
        union colour
        union summary
        AddrCopy(*newRef = *ref)
        ShieldCover(newRef)
        BUFFER_COMMIT
        (*pool->format->move)(ref, newRef)
        ref = newRef
      yes:
        /* Snap out */
        ref = newRef
    ShieldCover(ref)

B. Document History

  2007-01-12  RHSK  Created.
  2007-01-16  RHSK  .scan.loop, .scan.total.
  2007-01-18  RHSK  .scan.change.

C. Copyright and License

This document is copyright © 2007 Ravenbrook Limited. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Redistributions in any form must be accompanied by information on how to obtain complete source code for the this software and any accompanying software that uses this software. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.

This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, or non-infringement, are disclaimed. In no event shall the copyright holders and contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.