How to end all threads once the first has returned

I'm relatively new to multithreading, as this post may suggest, but I'm trying to have threads end once the nonce in the following code is found.

I'm also having a bit of trouble actually getting the values out from said threads

Any other suggestions regarding said code are also very much appreciated!

use num_bigint::BigUint;
use num_traits::One;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use hex::ToHex;
use std::time::Instant;
use std::thread;
use std::sync::{Arc, Mutex};

//todo: move into inputs
const LEADING_ZEROS: usize = 5;
const MAX_NONCE: u64 = 1_000_000_000;
 
fn main() {
    
    //todo add back in where appropriate
    //let before = Instant::now();
    //println!("Elapsed time: {:2?}", before.elapsed());
    let input = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
    let mut vec: Vec<&str> = Vec::with_capacity(LEADING_ZEROS * 3); //TODO: limit threads
    for _ in 0..LEADING_ZEROS * 3 {
        vec.push(input);
    }
    //
    let mut lower = 0;
    let mut upper = MAX_NONCE / (LEADING_ZEROS as u64 * 3);
    let noncer = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for i in vec {
        let noncer = Arc::clone(&noncer);
        let handle = thread::spawn(move || {
            find_nonce(i, lower, upper)
        });
        
        handles.push(handle);
        
        let temp = lower;
        lower = upper;
        upper = upper + temp;
        
    }
    let nonce_vec: Vec<u64> = vec![];
    for handle in handles {

        println!("handle? {:?}", handle);
        handle.join().unwrap();
    }
    //println!("handle? {:?}", handles);
    println!("nonce: {}", *noncer.lock().unwrap());
    let max_hash = BigUint::one() << (256 - 4 * LEADING_ZEROS); //>
    
    
   

    
    
    //println!("{:?}", nonce);
    println!("old    hash: {}", hash_without_nonce(input));
    //println!("nonced hash: {}", nonce_hash(input, nonce));

}

const HASH_BYTE_SIZE: usize = 32;

pub type Sha256Hash = [u8; HASH_BYTE_SIZE];

pub fn find_nonce(input: &str, lower: u64, upper: u64) -> u64 {
    let mut nonce = 0;
    //println!("{}", input);
    for nonces in lower..upper {
        //println!("{}", nonces);
        let max_hash = BigUint::one() << (256 - 4 * LEADING_ZEROS); //>
        if found_nonce(input, nonces, max_hash) {
            nonce = nonces;
            return nonce
        }
    }
    nonce
}

pub fn nonce_hash(input: &str, nonce: u64) -> String {
    let mut contents = Vec::new();
    contents.extend_from_slice(input.as_bytes());
    contents.extend_from_slice(&convert_u64_little_endian(nonce));
    let mut hasher = Sha256::new();
    hasher.input(&contents);
    let mut hash = Sha256Hash::default();
    hasher.result(&mut hash);
    let result: String = hash.to_hex();
    result
}

pub fn found_nonce(input: &str, nonce: u64, max_hash: BigUint) -> bool {
    let mut contents = Vec::new();
    contents.extend_from_slice(input.as_bytes());
    contents.extend_from_slice(&convert_u64_little_endian(nonce));
    let mut hasher = Sha256::new();
    hasher.input(&contents);
    let mut hash = Sha256Hash::default();
    hasher.result(&mut hash);
    let hash_int = BigUint::from_bytes_be(&hash);
    //println!("{}", hash.to_hex());
    if hash_int < max_hash {
        println!("{}", nonce);
        true
    } else {
        false
    }
    

}

pub fn hash_without_nonce(input: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.input(input.as_bytes());
    let mut hash = Sha256Hash::default();
    hasher.result(&mut hash);
    let result = hash.to_hex();
    result
}

pub fn convert_u64_little_endian(val: u64) -> [u8; 8] {
    return [
        val as u8,
        (val >> 8) as u8,
        (val >> 16) as u8,
        (val >> 24) as u8,
        (val >> 32) as u8,
        (val >> 40) as u8,
        (val >> 48) as u8,
        (val >> 56) as u8,
    ]
}

For getting values in and out of threads you'd typically use channels. The best crate for this is crossbeam-channel. This includes telling threads to quit, because you can't force-end a thread externally.

Alternatively, if you have data you just want to process using multiple threads, don't bother spawning and managing them manually. Use rayon.

2 Likes

The concept you need here is "cooperative cancellation". Generally, it's not possible to make a thread stop.
What you can to is to set some shared flag to true, and make the worker threads check it periodically.

Here's an example:

Note that the example uses a global variable for simplicity. For production code, you want to change that to Arc<AtomicBool>.

As a more general note, it's useful to minimize example before posting the question: that way, it's easier to understand at a glance what the problem is exactly, and minimization often helps to just the answer the question for yourself.

2 Likes

I ended up using the rayon crate, and crossbeam_channel for something else in the project, so thanks for the tips on that

For future readers:

use rayon::prelude::*;

pub fn find_nonce(input: &str, &mut nonce_vec: Vec<u64>) -> u64 {
    let nonce_wrapped = nonce_vec.par_iter().find_any(|&&nonce| found_nonce(input, nonce));
    let nonce = *nonce_wrapped.unwrap_or(&MAX_NONCE);
    nonce
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.