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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

use std::ops::Sub;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Condvar;
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicBool;
use std::time::Duration;
use std::time::Instant;

use crate::Error;
use crate::net::Packet;
use crate::net::BinaryReader;

pub const BLOCK_COUNT      : usize    = 255;
pub const BLOCK_OFFSET     : usize    =   1;
pub const WAIT_TIME_MILLIS : Duration = Duration::from_secs(3);


pub struct BlockManager {
    blocks: Vec<Arc<BlockInner>>
}

impl Default for BlockManager {
    fn default() -> Self {
        BlockManager {
            blocks: {
                let mut vec = Vec::with_capacity(BLOCK_COUNT);
                for id in 0..BLOCK_COUNT as u8 {
                    vec.push(Arc::new({
                        let mut inner = BlockInner::default();
                        inner.id = id + BLOCK_OFFSET as u8;
                        inner
                    }));
                }
                vec
            }
        }
    }
}

impl BlockManager {
    pub fn block(&self) -> Result<Block, Error> {
        let inner = self.blocks
            .iter()
            .find_map(|inner|
                if inner.try_activate() {
                    Some(inner.clone())
                } else {
                    None
                }
            )
            .ok_or(Error::NoFreeSlots)?;
        Ok(Block {
            inner,
            timestamp: Instant::now(),
        })
    }

    pub fn answer(&self, response: Packet) {
        let id = response.session() as usize;
        self.blocks[id - BLOCK_OFFSET].answer(response);
    }
}


pub struct Block {
    inner: Arc<BlockInner>,
    timestamp: Instant,
}

impl Block {

    pub fn id(&self) -> u8 {
        self.inner.id
    }

    pub fn wait(&mut self) -> Result<Packet, Error> {
        let packet : Packet = match self.inner.r#await(self.timestamp, WAIT_TIME_MILLIS) {
            Some(packet) => packet,
            None => return Err(Error::Timeout)
        };

        if packet.command() == 0xFF { // error
            if packet.path_sub() == 0xFF { // server error
                let reader : &mut BinaryReader = &mut packet.read();
                return Err(Error::ServerError {
                    exception_type: reader.read_string()?,
                    message: reader.read_string()?,
                    stack_trace: reader.read_string()?
                });

            } else {
                return Err(Error::error_code(packet.path_sub()))
            }
        }

        Ok(packet)
    }
}

impl Drop for Block {
    fn drop(&mut self) {
        self.inner.deactivate();
    }
}


#[derive(Default)]
struct BlockInner {
    id: u8,
    active: AtomicBool,
    data: Mutex<Option<Packet>>,
    cond: Condvar,
}

impl BlockInner {
    /// Tries to activate this block and returns whether the operation was successful
    fn try_activate(&self) -> bool {
        !self.active.swap(true, Ordering::Relaxed)
    }

    fn deactivate(&self) {
        {
            let mut lock = self.data.lock().expect("Failed to acquire lock for BlockInner::data");
            *lock = None;
        }
        self.active.store(false, Ordering::Relaxed);
    }

    fn answer(&self, packet: Packet) {
        {
            let mut lock = self.data.lock().expect("Failed to acquire lock for BlockInner::data");
            *lock = Some(packet);
        }
        self.cond.notify_all();
    }

    fn r#await(&self, time: Instant, timeout: Duration) -> Option<Packet> {
        let mut lock = self.data.lock().expect("Failed to acquire lock for BlockInner::data");
        let mut elapsed = time.elapsed();
        while elapsed < timeout {
            let (returned_lock, _timeout_result) = self.cond.wait_timeout(lock, timeout.sub(elapsed)).expect("wait_timeout failed");
            lock = returned_lock;
            if let Some(response) = lock.take() {
                return Some(response);
            }
            elapsed = time.elapsed();
        }
        None
    }
}