The Computer Language
Benchmarks Game

thread-ring Rust #2 program

source code

// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers
// contributed by Joshua Landau

// Custom locks for 2-stage locking
mod locks {
   use std::sync::{Condvar, Mutex};
   use std::sync::atomic::{AtomicUsize, Ordering};
   use std::thread;

   pub struct Lock {
      condvar: Condvar,
      is_set: Mutex<bool>
   }

   impl Lock {
      pub fn new(unlocked: bool) -> Lock {
         Lock { condvar: Condvar::new(), is_set: Mutex::new(unlocked) }
      }

      pub fn lock(&self) {
         let mut set = self.is_set.lock().unwrap();
         while !*set {
            set = self.condvar.wait(set).unwrap();
         }
         *set = false;
      }

      pub fn unlock(&self) {
         let mut set = self.is_set.lock().unwrap();
         *set = true;
         self.condvar.notify_one();
      }
   }

   const EMPTY: usize = ::std::usize::MAX;
   pub struct SpinLock(AtomicUsize);

   impl SpinLock {
      pub fn new(value: Option<usize>) -> SpinLock {
         SpinLock(AtomicUsize::new(value.unwrap_or(EMPTY)))
      }

      pub fn lock(&self) -> usize {
         loop {
            let gotten = self.0.swap(EMPTY, Ordering::SeqCst);
            if gotten != EMPTY {
               return gotten;
            }
            thread::yield_now();
         }
      }

      pub fn unlock(&self, value: usize) {
         self.0.store(value, Ordering::SeqCst);
      }
   }
}

use std::sync::Arc;
use std::thread;

use locks::{Lock, SpinLock};

fn start(n_tasks: usize, token: usize) {
   let locks: Vec<_> = (0..n_tasks).map(|i|
      Arc::new(Lock::new(i == 1 || i == 2))
   ).collect();

   let io: Vec<_> = (0..n_tasks).map(|i|
      Arc::new(SpinLock::new(if i == 1 { Some(token) } else { None }))
   ).collect();

   let threads: Vec<_> = (0..n_tasks).map(|i| {
      let lock   = locks[i].clone();
      let input  = io[i].clone();
      let output = io[(i + 1) % n_tasks].clone();
      let unlock = locks[(i + 2) % n_tasks].clone();

      thread::spawn(move || roundtrip(i + 1, lock, input, output, unlock))
   }).collect();

   for thread in threads {
      thread.join().unwrap();
   }
}

fn roundtrip(
   thread_id: usize,
   lock:   Arc<Lock>,
   input:  Arc<SpinLock>,
   output: Arc<SpinLock>,
   unlock: Arc<Lock>,
) {
   loop {
      lock.lock();
      let input_value = input.lock();
      output.unlock(input_value.saturating_sub(1));
      unlock.unlock();

      if input_value == 1 { println!("{}", thread_id); }
      if input_value <= 1 { return; }
   }
}

fn main() {
   let args = &mut std::env::args_os();
   let token = args.skip(1).next()
      .and_then(|s| s.into_string().ok())
      .and_then(|n| n.parse().ok())
      .unwrap_or(1000);
   let n_tasks = args.next()
      .and_then(|s| s.into_string().ok())
      .and_then(|n| n.parse().ok())
      .unwrap_or(503);
   start(n_tasks, token);
}
    

notes, command-line, and program output

NOTES:
64-bit Ubuntu quad core
rustc 1.25.0 (84203cac6 2018-03-25)


Thu, 29 Mar 2018 17:20:34 GMT

MAKE:
/opt/src/rust-1.25.0/bin/rustc -C opt-level=3 -C target-cpu=core2 -C lto -C codegen-units=1  threadring.rs -o threadring.rust-2.rust_run

5.73s to complete and log all make actions

COMMAND LINE:
./threadring.rust-2.rust_run 50000000

PROGRAM OUTPUT:
292