use log::{info, trace};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use crate::crack::indices::{indices_create, indices_increment_by, indices_to_string};
use crate::{CrackTarget, InternalCrackParameter};
pub(crate) fn spawn_worker_threads<T: CrackTarget>(
params: Arc<InternalCrackParameter<T>>,
done: Arc<AtomicBool>,
) -> Vec<JoinHandle<Option<String>>> {
let mut handles = vec![];
for tid in 0..params.thread_count() {
let mut indices = indices_create(
params.crack_param().max_length(),
params.crack_param().min_length(),
);
indices_increment_by(params.crack_param().alphabet(), &mut indices, tid)
.expect("Increment failed");
handles.push(spawn_worker_thread(
params.clone(),
done.clone(),
indices,
tid,
));
}
handles
}
fn spawn_worker_thread<T: CrackTarget>(
params: Arc<InternalCrackParameter<T>>,
done: Arc<AtomicBool>,
mut indices: Box<[isize]>,
tid: usize,
) -> JoinHandle<Option<String>> {
let mut iteration_count = 0;
thread::spawn(move || {
let mut current_crack_string = String::with_capacity(indices.len() * 4);
let mut result = None;
const INTERRUPT_COUNT_THRESHOLD: usize = 2_000_000;
let mut interrupt_count = INTERRUPT_COUNT_THRESHOLD;
loop {
{
if interrupt_count == 0 {
interrupt_count = INTERRUPT_COUNT_THRESHOLD;
if done.load(Ordering::SeqCst) {
trace!("Thread {:>2} stops at {:>6.2}% progress because another thread found a solution", tid, get_percent(¶ms, iteration_count));
break;
} else {
trace!(
"Thread {:>2} is at {:>6.2}% progress",
tid,
get_percent(¶ms, iteration_count)
);
}
}
interrupt_count -= 1;
}
{
let res = indices_increment_by(
params.crack_param().alphabet(),
&mut indices,
params.thread_count(),
);
if res.is_err() {
info!(
"Thread {:>2} checked all possible values without finding a solution. Done.",
tid
);
break;
}
iteration_count += 1;
indices_to_string(
&mut current_crack_string,
params.crack_param().alphabet(),
&indices,
);
if params.hash_matches(current_crack_string.as_str()) {
info!(
"Thread {:>2} found a solution at a progress of {:>6.2}%!",
tid,
get_percent(¶ms, iteration_count)
);
done.store(true, Ordering::SeqCst);
result = Some(current_crack_string);
break;
}
}
}
result
})
}
#[inline]
fn get_percent<T: CrackTarget>(cp: &InternalCrackParameter<T>, iteration_count: u64) -> f32 {
let total = cp.combinations_p_t() as f32;
let current = iteration_count as f32;
current / total * 100.0
}