pub fn combination_sync<'a, T>(
    domain: &'a [T],
    r: usize,
    result: Arc<RwLock<Vec<&'a T>>>,
    cb: impl FnMut()
)
Expand description

Deprecated

See combination function for reason of deprecation

Similar to combination function except the way it return the combination. It return result through Rc<RefCell<>> to mutable slice of result. It’ll notify caller on each new result via empty callback function.

Parameters

  • domain A raw data to get combination.
  • r A size of each combination.
  • result An Rc<RefCell<>> to mutable slice of length equals to r
  • cb A callback function which will be called after new combination in result is set.

Return

This function return result through function’s parameter result and notify caller that new result is available through cb callback function.

Rationale

The combination function return value in callback parameter. It limit the lifetime of return combination to be valid only inside it callback. To use it on different thread, it need to copy the value which will have performance penalty. This function provide alternative way to share data between thread. It will write new result into Arc<RwLock<>> of Vec owned inside RwLock. Since it write data directly into Vec, other threads won’t know that new data is wrote. The combination generator thread need to notify other threads via channel. This introduce another problem. Since combination generator write data directly into shared memory address, it need to know if all other threads are done using the data. Otherwise, in worst case, combination generator thread may dominate all other threads and hold write lock until it done generate every value. To solve such issue, combination generator thread need to get notified when each thread has done using the data.

Example

The scenario is we want to get combination from single source of data then distribute the combination to two workers which read each combination then do something about it, which in this example, simply print it.

   use permutator::combination_sync;
   use std::fmt::Debug;
   use std::sync::{Arc, RwLock};
   use std::sync::mpsc;
   use std::sync::mpsc::{Receiver, SyncSender};
   use std::thread;
 
   fn start_combination_process<'a>(data : &'a[i32], cur_result : Arc<RwLock<Vec<&'a i32>>>, k : usize, notifier : Vec<SyncSender<Option<()>>>, release_recv : Receiver<()>) {
       use std::time::Instant;
       let timer = Instant::now();
       let mut counter = 0;
       combination_sync(data, k, cur_result, || {
           notifier.iter().for_each(|n| {
               n.send(Some(())).unwrap(); // notify every thread that new data available
           });

           for _ in 0..notifier.len() {
               release_recv.recv().unwrap(); // block until all thread reading data notify on read completion
           }

           counter += 1;
       });

       notifier.iter().for_each(|n| {n.send(None).unwrap()}); // notify every thread that there'll be no more data.

       println!("Done {} combinations with 2 workers in {:?}", counter, timer.elapsed());
   }
   let k = 3;
   let data = &[1, 2, 3, 4, 5];
   let result = vec![&data[0]; k];
   let result_sync = Arc::new(RwLock::new(result));

   // workter thread 1
   let (t1_send, t1_recv) = mpsc::sync_channel::<Option<()>>(0);
   let (main_send, main_recv) = mpsc::sync_channel(0);
   let t1_local = main_send.clone();
   let t1_dat = Arc::clone(&result_sync);
   thread::spawn(move || {
       while let Some(_) = t1_recv.recv().unwrap() {
           let result : &Vec<&i32> = &*t1_dat.read().unwrap();
           println!("Thread1: {:?}", result);
           t1_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
       }
       println!("Thread1 is done");
   });

   // worker thread 2
   let (t2_send, t2_recv) = mpsc::sync_channel::<Option<()>>(0);
   let t2_dat = Arc::clone(&result_sync);
   let t2_local = main_send.clone();
   thread::spawn(move || {
       while let Some(_) = t2_recv.recv().unwrap() {
           let result : &Vec<&i32> = &*t2_dat.read().unwrap();
           println!("Thread2: {:?}", result);
           t2_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
       }
       println!("Thread2 is done");
   });

   // main thread that generate result
   thread::spawn(move || {
       start_combination_process(data, result_sync, k, vec![t1_send, t2_send], main_recv);
   }).join().unwrap();

See