parallel_task 0.6.0

A fast data parallelism library for Rust
Documentation
//! LimitAccessQueue is where the queue is stored and managed. This may only be accessed via the accessors.
use crate::utils::SpinWait;
use super::read_accessor::*;
use std::sync::{atomic::AtomicBool, Arc};

pub struct LimitAccessQueue<T,State> {
    pub val: Vec<T>,    
    write_block: AtomicBool,
    state: State
}

#[allow(dead_code,clippy::new_ret_no_self)]
impl<T,State> LimitAccessQueue<T,State> 
where State: Default + Clone
{
    pub fn new() -> (PrimaryAccessor<T,State>,SecondaryAccessor<T,State>) {
        let arc_obj = Arc::new(Self {
            val: Vec::new(),            
            write_block: AtomicBool::new(false),            
            state: State::default()            
        });        
        
        //we need to ensure the object within AtomicPtr survives on the heap and beyond
        //the function stack.                   
        let primary = ReadAccessor::new(arc_obj.clone(),ReadAccessorType::Primary);
        let secondary = ReadAccessor::new(arc_obj,ReadAccessorType::Secondary);             
        (PrimaryAccessor::new(primary), SecondaryAccessor::new(secondary))
                     
       
    }

    pub fn set_state(&mut self, state:State) {
        self.with_write_block(|s|{
            s.state = state;
        });
    }

    pub fn get_state(&mut self) -> State {
        self.with_write_block(|s|{
            s.state.clone()
        })
    }

    pub fn pop(&mut self) -> Option<T> {
        self.with_write_block(|s| { 
            s.val.pop()
        })       
    }

    pub fn pop_count(&mut self,count:usize) -> Option<Vec<T>> {
        self.with_write_block(|s| { 
            let mut res = Vec::new();
            for idx in 0..count {
                if let Some(val) = s.val.pop() {
                    res.push(val)
                } else {
                    if idx == 0 {
                        return None;
                    }
                    break;
                }
            }   
            Some(res)         
        })       
    }

    ///Steals all the un-popped values from the queue. It can then be reused
    /// elsewhere.
    /// ```
    /// use parallel_task::{
    /// accessors::limit_queue::LimitAccessQueue,
    /// push_workers::worker_thread::Coordination};
    /// let values = (0..100_000).collect::<Vec<_>>();
    /// let (mut primary, _) = LimitAccessQueue::<i32,Coordination>::new();
    /// _ = primary.write(values);
    /// let vec = primary.steal().unwrap(); //This step should not fail here. But unwrap not advised in production
    /// assert_eq!(vec.len(), 100_000);
    /// ```
    pub fn steal(&mut self) -> Option<Vec<T>> {         
        self.with_write_block(|s| {
            if s.val.is_empty() {
                None
            } else {
                // using mem swap to expedite the process
                let mut tmp:Vec<T> = Vec::with_capacity(1);
                std::mem::swap(&mut tmp, &mut s.val);
                Some(tmp)            
            }        
        })              
    }

    ///Steals half the un-popped values from the queue. It can then be reused
    /// elsewhere.
    /// ```
    /// use parallel_task::{
    /// accessors::limit_queue::LimitAccessQueue,
    /// push_workers::worker_thread::Coordination};
    /// let values = (0..100_000).collect::<Vec<_>>();
    /// let (mut primary, _) = LimitAccessQueue::<i32,Coordination>::new();
    /// _ = primary.write(values);
    /// let vec = primary.steal_half().unwrap(); //This step should not fail here. But unwrap not advised in production
    /// assert_eq!(vec.len(), 50_000);
    /// ```
    pub fn steal_half(&mut self) -> Option<Vec<T>> {          
        self.with_write_block(|s| {         
            if s.val.is_empty() {                
                None
            } 
            else {
                let res = s.val.split_off(s.val.len()/2);          
                Some(res)            
            }            
        })                      
    }

    pub fn is_empty(&mut self) -> bool { 
        self.len() == 0              
    }

    pub fn len(&mut self) -> usize {         
        self.with_write_block(|s|{
            if s.val.is_empty() { 0usize } else { s.val.len() }
        })        
    }

    pub fn atomic_write_block_to_true(&mut self) -> Result<bool, bool> {
        self.write_block.compare_exchange(false, true, std::sync::atomic::Ordering::SeqCst, std::sync::atomic::Ordering::SeqCst)
    }    

    pub fn with_write_block<F,Output>(&mut self, f:F) -> Output
    where F: FnOnce(&mut Self) -> Output {                          
        SpinWait::loop_while_mut(||self.atomic_write_block_to_true().is_err());                            
        let output = f(self);
        self.write_block.store(false, std::sync::atomic::Ordering::SeqCst);                 
        output
    }

    pub fn push(&mut self, value:T) {

        self.with_write_block(|s|
        {
            s.val.push(value);
        });        
    }

    pub fn write(&mut self, mut values:Vec<T>) {     
        self.with_write_block(|s|{
            let drained = values.drain(0..);                
            s.val.extend(drained); 
        });                                         
    }

    pub fn replace(&mut self, mut values:Vec<T>) {    
        self.with_write_block(|s|{             
            std::mem::swap(&mut values, &mut s.val);                      
        });                            
    }

    pub fn is_write_blocked(&self) -> bool {
        self.write_block.load(std::sync::atomic::Ordering::SeqCst)
    }

    // pub fn ingest_iter<I>(&mut self, mut i:I)
    // where I:AccessQueueIngestor<IngestorItem = T>
    // {
    //     while let Some(value) = i.next_chunk() {
    //         self.push(value);
    //     }
    // }

}