1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) 2022 MASSA LABS <info@massa.net>

#[cfg(test)]
mod tests;

mod controller;
mod draw;
mod worker;

use massa_hash::Hash;
use massa_models::{address::Address, slot::Slot};
use massa_pos_exports::{PosResult, Selection};

use parking_lot::{Condvar, Mutex, RwLock, RwLockReadGuard};
use std::{
    collections::{BTreeMap, HashMap, VecDeque},
    sync::Arc,
};

/// Enumeration of internal commands sent to the selector thread as input
/// data. `CycleInfo`, Look at `InputDataPtr`
pub(crate) enum Command {
    /// Input requirements for the draw
    DrawInput {
        cycle: u64,
        lookback_rolls: BTreeMap<Address, u64>,
        lookback_seed: Hash,
    },
    /// Stop the thread (usually sent by the manager and pushed at the top
    /// of the command queue)
    Stop,
}

/// Draw cache (lowest index = oldest)
#[derive(Debug)]
pub(crate) struct DrawCache(pub VecDeque<CycleDraws>);

impl DrawCache {
    /// Get the range of available cycles in the cache.
    /// Returns None if the cache is empty
    pub fn get_available_cycles_range(&self) -> Option<std::ops::RangeInclusive<u64>> {
        match self.0.front() {
            None => None,
            Some(cd) => {
                let upper_bound = cd
                    .cycle
                    .checked_add(self.0.len().try_into().expect("overflow on cycles length"))
                    .expect("overflow on cycle delta");
                Some(cd.cycle..=upper_bound)
            }
        }
    }

    /// get the index of a cycle in the cache
    pub fn get_cycle_index(&self, cycle: u64) -> Option<usize> {
        let first_cycle = match self.0.front() {
            Some(c) => c.cycle,
            None => return None, // history empty
        };
        if cycle < first_cycle {
            return None; // in the past
        }
        let index: usize = match (cycle - first_cycle).try_into() {
            Ok(idx) => idx,
            Err(_) => return None, // usize overflow
        };
        if index >= self.0.len() {
            return None; // in the future
        }
        Some(index)
    }

    /// get a reference to the draws of a given cycle
    pub fn get(&self, cycle: u64) -> Option<&CycleDraws> {
        self.get_cycle_index(cycle).and_then(|idx| self.0.get(idx))
    }
}

/// Draws for a cycle, used in selector cache
#[derive(Debug)]
pub(crate) struct CycleDraws {
    /// cycle number
    pub cycle: u64,
    /// cache of draws
    pub draws: HashMap<Slot, Selection>,
}

/// Structure of the shared pointer to the computed draws, or error if the draw system failed.
pub(crate) type DrawCachePtr = Arc<(RwLockCondvar, RwLock<PosResult<DrawCache>>)>;

/// Start thread selector
pub use worker::start_selector_worker;

// an RwLock condvar
#[derive(Default)]
struct RwLockCondvar {
    mutex: Mutex<()>,
    condvar: Condvar,
}

impl RwLockCondvar {
    fn wait<T>(&self, rwlock_read_guard: &mut RwLockReadGuard<T>) {
        let mutex_guard = self.mutex.lock();

        RwLockReadGuard::unlocked(rwlock_read_guard, || {
            let mut mutex_guard = mutex_guard;
            self.condvar.wait(&mut mutex_guard);
        });
    }

    fn notify_all(&self) {
        self.condvar.notify_all();
    }
}