// Create a new test with the given table style and object count. public TestSet(TableStyle ts, int count) { // Use one random number generator for the life of the test. Could support explicit seeds for // reproducible tests here. m_rng = new Random(); // Remember our parameters. m_count = count; m_style = ts; // Various arrays. m_nodes = new Node[count]; // The array of objects m_collected = new bool[count]; // Records whether each object has been collected (entries are set by // the finalizer on Node) m_marks = new bool[count]; // Array used during individual test runs to calculate whether each // object should still be alive (allocated once here to avoid // injecting further garbage collections at run time) // Allocate each object (Node). Each knows its own unique ID (the index into the node array) and has a // back pointer to this test object (so it can phone home to report its own collection at finalization // time). for (int i = 0; i < count; i++) { m_nodes[i] = new Node(this, i); } // Determine how many handles we need to allocate given the number of nodes. This varies based on the // table style. switch (ts) { case TableStyle.Unconnected: // Primaries and secondaries are completely different objects so we split our nodes in half and // allocate that many handles. m_handleCount = count / 2; break; case TableStyle.ForwardLinked: // Nodes are primaries in one handle and secondary in another except one that falls off the end. // So we have as many handles as nodes - 1. m_handleCount = count - 1; break; case TableStyle.BackwardLinked: // Nodes are primaries in one handle and secondary in another except one that falls off the end. // So we have as many handles as nodes - 1. m_handleCount = count - 1; break; case TableStyle.Random: // Each node is a primary in some handle (secondaries are selected from amongst all the same nodes // randomly). So we have as many nodes as handles. m_handleCount = count; break; } // Allocate an array of HandleSpecs. These aren't the real handles, just structures that allow us // remember what's in each handle (in terms of the node index number for the primary and secondary). // We need to track this information separately because we can't access the real handles directly // (ConditionalWeakTable hides them) and we need to recall exactly what the primary and secondary of // each handle is so we can compute our own notion of object liveness later. m_handles = new HandleSpec[m_handleCount]; // Initialize the handle specs to assign objects to handles based on the table style. for (int i = 0; i < m_handleCount; i++) { int primary = -1, secondary = -1; switch (ts) { case TableStyle.Unconnected: // Assign adjacent nodes to the primary and secondary of each handle. primary = i * 2; secondary = (i * 2) + 1; break; case TableStyle.ForwardLinked: // Primary of each handle is the secondary of the last handle. primary = i; secondary = i + 1; break; case TableStyle.BackwardLinked: // Primary of each handle is the secondary of the next handle. primary = i + 1; secondary = i; break; case TableStyle.Random: // Primary is each node in sequence, secondary is any of the nodes randomly. primary = i; secondary = m_rng.Next(m_handleCount); break; } m_handles[i].Set(primary, secondary); } // Allocate a ConditionalWeakTable mapping Node keys to Node values. m_table = new ConditionalWeakTable <Node, Node>(); // Using our handle specs computed above add each primary/secondary node pair to the // ConditionalWeakTable in turn. This causes the ConditionalWeakTable to allocate a dependent handle // for each entry with the primary and secondary objects we specified as keys and values (note that // this scheme prevents us from creating multiple handles with the same primary though if this is // desired we could achieve it by allocating multiple ConditionalWeakTables). for (int i = 0; i < m_handleCount; i++) { m_table.Add(m_nodes[m_handles[i].m_primary], m_nodes[m_handles[i].m_secondary]); } }
// Create a new test with the given table style and object count. public TestSet(TableStyle ts, int count) { // Use one random number generator for the life of the test. Could support explicit seeds for // reproducible tests here. m_rng = new Random(); // Remember our parameters. m_count = count; m_style = ts; // Various arrays. m_nodes = new Node[count]; // The array of objects m_collected = new bool[count]; // Records whether each object has been collected (entries are set by // the finalizer on Node) m_marks = new bool[count]; // Array used during individual test runs to calculate whether each // object should still be alive (allocated once here to avoid // injecting further garbage collections at run time) // Allocate each object (Node). Each knows its own unique ID (the index into the node array) and has a // back pointer to this test object (so it can phone home to report its own collection at finalization // time). for (int i = 0; i < count; i++) m_nodes[i] = new Node(this, i); // Determine how many handles we need to allocate given the number of nodes. This varies based on the // table style. switch (ts) { case TableStyle.Unconnected: // Primaries and secondaries are completely different objects so we split our nodes in half and // allocate that many handles. m_handleCount = count / 2; break; case TableStyle.ForwardLinked: // Nodes are primaries in one handle and secondary in another except one that falls off the end. // So we have as many handles as nodes - 1. m_handleCount = count - 1; break; case TableStyle.BackwardLinked: // Nodes are primaries in one handle and secondary in another except one that falls off the end. // So we have as many handles as nodes - 1. m_handleCount = count - 1; break; case TableStyle.Random: // Each node is a primary in some handle (secondaries are selected from amongst all the same nodes // randomly). So we have as many nodes as handles. m_handleCount = count; break; } // Allocate an array of HandleSpecs. These aren't the real handles, just structures that allow us // remember what's in each handle (in terms of the node index number for the primary and secondary). // We need to track this information separately because we can't access the real handles directly // (ConditionalWeakTable hides them) and we need to recall exactly what the primary and secondary of // each handle is so we can compute our own notion of object liveness later. m_handles = new HandleSpec[m_handleCount]; // Initialize the handle specs to assign objects to handles based on the table style. for (int i = 0; i < m_handleCount; i++) { int primary = -1, secondary = -1; switch (ts) { case TableStyle.Unconnected: // Assign adjacent nodes to the primary and secondary of each handle. primary = i * 2; secondary = (i * 2) + 1; break; case TableStyle.ForwardLinked: // Primary of each handle is the secondary of the last handle. primary = i; secondary = i + 1; break; case TableStyle.BackwardLinked: // Primary of each handle is the secondary of the next handle. primary = i + 1; secondary = i; break; case TableStyle.Random: // Primary is each node in sequence, secondary is any of the nodes randomly. primary = i; secondary = m_rng.Next(m_handleCount); break; } m_handles[i].Set(primary, secondary); } // Allocate a ConditionalWeakTable mapping Node keys to Node values. m_table = new ConditionalWeakTable<Node, Node>(); // Using our handle specs computed above add each primary/secondary node pair to the // ConditionalWeakTable in turn. This causes the ConditionalWeakTable to allocate a dependent handle // for each entry with the primary and secondary objects we specified as keys and values (note that // this scheme prevents us from creating multiple handles with the same primary though if this is // desired we could achieve it by allocating multiple ConditionalWeakTables). for (int i = 0; i < m_handleCount; i++) m_table.Add(m_nodes[m_handles[i].m_primary], m_nodes[m_handles[i].m_secondary]); }