Skip to content
GraphMarkNodeForDeletion

GraphMarkNodeForDeletion

Description

Mark a node for deletion on the next GraphCommitChanges.

Marked nodes remain visible until commit. This operation is safe during graph traversal and is intended for destructive passes that need stable iteration.

Parameters

Name Direction Description
node in GraphNode handle to mark.

Success

Returns true. The slot referenced by node is now flagged as marked; the graph’s pending-delete count grows by one. live_count is unchanged - the node is still observable through traversal and lookup until GraphCommitChanges runs.

Failure

Returns false when the node was already marked. The graph is not modified.

Usage example (Cross-references)

Usage examples (Cross-references)
    }
    
    bool GraphMarkNodeForDeletion(GraphNode node) {
        GenericGraph     *graph;
        GenericGraphSlot *slot;
    
    static bool test_graph_mark_delete_commit_and_reuse(void) {
        WriteFmt("Testing GraphMarkNodeForDeletion and GraphCommitChanges\n");
    
        DefaultAllocator alloc = DefaultAllocatorInit();
        GraphForeachNode(&graph, node) {
            if (GraphNodeData(&graph, node) == 20) {
                GraphMarkNodeForDeletion(node);
            }
        }
    
        bool result = !GraphNodeMarkedForDeletion(node);
        result      = result && GraphMarkNodeForDeletion(node);
        result      = result && GraphNodeMarkedForDeletion(node);
        result      = result && !GraphMarkNodeForDeletion(node);
        result      = result && GraphMarkNodeForDeletion(node);
        result      = result && GraphNodeMarkedForDeletion(node);
        result      = result && !GraphMarkNodeForDeletion(node);
        result      = result && GraphUnmarkNodeForDeletion(node);
        result      = result && !GraphNodeMarkedForDeletion(node);
    
        bool result = GraphMarkEdgeForRemoval(&graph, a, b);
        result      = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && GraphEdgeMarkedForRemoval(&graph, a, b);
        result      = result && GraphNodeMarkedForDeletion(GraphGetNode(&graph, b));
        counts[GraphNodeIdIndex(b)] = 29;
    
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
        result      = result && !GraphContainsNode(&graph, b);
        GraphNode   node = GraphGetNode(&graph, a);
    
        (void)GraphMarkNodeForDeletion(node);
        (void)GraphCommitChanges(&graph);
        (void)GraphNodeVisit(node);
        GraphAddEdge(&graph, a, b);
    
        GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
    
        u64 committed = GraphCommitChanges(&graph);
    
        // Delete b so its slot is free at the time GraphClear walks it.
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
        result      = result && !GraphContainsNode(&graph, b);
    
        // One deletion leaves a single entry in free_indices going into clear.
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
    
        // First commit frees b's slot (free-list entry, generation bumped).
        bool result  = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        u64  removed = GraphCommitChanges(&graph);
        result       = result && (removed == 1);
        // Second commit: a free slot (b's) is present. The marked/unmarked passes
        // iterate every slot; the free slot must be skipped, not validated.
        result  = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        removed = GraphCommitChanges(&graph);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot, a is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse b's slot, b is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot, a is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse b's slot, b is now stale
        // Mark a (index 0, in range after the shrink) so the commit has a node to
        // delete (pending_delete_count > 0, so it does not early-return).
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
    
        // Clear the memoized validated bit on a consistent graph so commit's entry
        // Mark both a (in range after the shrink) and c (the slot we hide at
        // index == len). pending_delete_count = 2 so commit does real work.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        // index == len). pending_delete_count = 2 so commit does real work.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
    
        // Clear the memoized validated bit on a consistent graph.
        result = result && GraphAddEdge(&graph, third_id, third_id);
        result = result && (GraphNodeVisit(GraphGetNode(&graph, first_id)) == 1);
        result = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, second_id));
        result = result && GraphMarkEdgeForRemoval(&graph, third_id, third_id);
        (void)a;
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)a;
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
    
        // Delete + commit b so its slot lands on the free list (reuse target).
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
        (void)a;
    
        GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
    
        // A structural mutation that keeps b marked but re-arms the deep
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)a; // a keeps slot 0 occupied; only b is deleted to seed free_indices.
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        (void)GraphCommitChanges(&graph);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        (void)GraphCommitChanges(&graph);
        // Delete the middle node so its slot becomes a free hole between two
        // live slots, then commit so the deletion materializes.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        // id is stale (slot occupied, generation superseded).
        GraphNode stale = GraphGetNode(&graph, a);
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot at a higher generation
            // Mark + commit a deletion mid-iteration. The commit bumps the
            // mutation epoch, so the iterator's next step must abort.
            (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
            (void)GraphCommitChanges(&graph);
        }
        // Delete + commit: slot 0 becomes free and its generation is bumped to
        // GraphNodeIdGeneration(a) + 1.
        result = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        result = result && (GraphCommitChanges(&graph) == 1);
        result = result && !GraphContainsNode(&graph, a);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        // Reuse a's slot at a higher generation; `a` is now stale.
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
Last updated on