pargraph 0.2.0

Operator based parallel graph processing.
Documentation
// SPDX-FileCopyrightText: 2023 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: GPL-3.0-or-later

//! Single-threaded executor implementation.

use petgraph::{data::DataMap, visit::GraphBase};

use crate::{
    BorrowDataCell, LabellingOperator,
    local_view::LocalGraphView,
    worklists::{PushWrapper, Worklist, WorklistChannel},
};
use std::{borrow::Borrow, fmt::Debug, ops::DerefMut};

/// Executes graph operators on the current thread.
#[derive(Default)]
pub struct SingleThreadExecutor {}

impl SingleThreadExecutor {
    /// Create a new executor.
    pub fn new() -> Self {
        Self {}
    }
}

impl SingleThreadExecutor {
    /// Run an algorithm which modifies node labels but keeps the topology unchanged.
    pub fn run_node_labelling<Wl, Op, G>(&self, mut initial_worklist: Wl, operator: Op, graph: G)
    where
        Wl: Worklist<Op::WorkItem>,
        Op: LabellingOperator<G>,
        G: GraphBase + DataMap,
        G::NodeWeight: BorrowDataCell<UserData = Op::NodeWeight>,
        G::NodeId: Debug,
        G::EdgeId: Debug,
    {
        let wl_channel = initial_worklist.create_channel();

        while let Some(work_item) = wl_channel.pop() {
            // Unsafely get mutable access to the graph data.
            let active_node = *work_item.borrow();
            let node_data = graph.node_weight(active_node).expect("node has no data");

            let local_view = LocalGraphView::new(&graph, active_node);

            let mut data_guard = node_data.borrow_data_cell().try_write(0).expect(
                "there should be no other access to the node data when using a single thread",
            );

            operator
                .op(work_item, local_view, data_guard.deref_mut(), PushWrapper::new(&wl_channel, 0))
                .expect("data conflicts should not happen in the single threaded executor. Did the operator cause it?");
        }

        initial_worklist.stop();
    }
}