rusty_mos/memory/
shared_pool.rs

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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! Support the shared momeory pool. We can share more than one pages between
//! envs.

extern crate alloc;

use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::SeqCst;

use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;

use crate::consts::error::KError;
use crate::debugln;
use crate::process::envs::{LOG2NENV, NENV};
use crate::utils::sync_ref_cell::SyncImplRef;

/// Memory Pool Entry. Contains the pages it shared and the envs attatched to
/// it. The entry also contains a lock field.
struct MemoryPoolEntry {
    /// List of page *indexes* shared by this pool.
    pages: Vec<usize>,
    /// List of envs bind to this pool.
    envs: Vec<usize>,
    /// The reference count of this pool. Destroy the pool is the reference
    /// goes *zero*.
    reference: usize,
    /// Write lock, store the env-id which locked it. Or *zero* means unlocked.
    write_lock: AtomicUsize,
}

impl Default for MemoryPoolEntry {
    /// Default comstructions.
    fn default() -> Self {
        Self::new()
    }
}

impl MemoryPoolEntry {
    /// Create a new pool.
    pub const fn new() -> Self {
        Self {
            reference: 0,
            pages: Vec::new(),
            envs: Vec::new(),
            write_lock: AtomicUsize::new(0),
        }
    }

    /// Decrease the reference of the pool. If the `reference` is minused to
    /// *zero*, an `Ok(true)` will be returned.
    fn deref(&mut self, envid: usize) -> Result<bool, KError> {
        if !self.envs.contains(&envid) {
            Err(KError::PoolNotBind)
        } else {
            self.reference -= 1;
            let _ = self.write_lock.compare_exchange(envid, 0, SeqCst, SeqCst);
            Ok(self.reference == 0)
        }
    }

    /// Try to lock the pool. If locked successfully, the return value is
    /// `true`.
    pub fn lock(&mut self, envid: usize) -> bool {
        self.write_lock.compare_exchange(0, envid, SeqCst, SeqCst) == Ok(0)
    }

    /// Unlock the pool. Only the env who locked it can unlock it.
    ///
    /// # Return
    ///
    /// If unlocked successfully, an `Ok(())` is returned.
    ///
    /// Otherwise, a [KError] with a `Err` wrapper is returned:
    /// - `Err(KError::NoLock)`: The pool was not locked.
    /// - `Err(KError::LockByOthers)`: The pool was locked by another env.
    pub fn unlock(&mut self, envid: usize) -> Result<(), KError> {
        let ret = self.write_lock.compare_exchange(envid, 0, SeqCst, SeqCst);
        if let Err(r) = ret {
            if r == 0 {
                return Err(KError::NoLock);
            } else {
                return Err(KError::LockByOthers);
            }
        }
        Ok(())
    }
}

/// Memory pool manager. Support every operation that is needed to perform.
pub struct MemoryPool {
    /// The memory pools. Map from pool_id to [MemoryPoolEntry].
    pools: BTreeMap<usize, MemoryPoolEntry>,
    envs: BTreeMap<usize, Vec<usize>>,
}

impl Default for MemoryPool {
    /// Default constructions
    fn default() -> Self {
        Self::new()
    }
}

impl MemoryPool {
    /// Create a new pool manager.
    pub const fn new() -> Self {
        Self {
            pools: BTreeMap::new(),
            envs: BTreeMap::new(),
        }
    }

    /// Get a new pool id and create a new pool entry.
    pub fn crate_pool(&mut self, envid: usize) -> usize {
        let id = mkpoolid(envid);
        self.pools.insert(id, MemoryPoolEntry::new());
        id
    }

    /// Insert a page into a pool. If the pool is not found, a
    /// `Err(KError::PoolNotFound)` will be returned.
    pub fn insert_page(&mut self, poolid: usize, pageid: usize) -> Result<(), KError> {
        match self.pools.get_mut(&poolid) {
            None => Err(KError::PoolNotFound),
            Some(pool) => {
                pool.pages.push(pageid);
                Ok(())
            }
        }
    }

    /// Bind the forked child env into the parent env's pools.
    ///
    /// Since when forked, the parent's pages will be dupped to the child's, so
    /// we need to fork the pools meanwhile. The `reference` of each pool will
    /// be increased.
    pub fn fork_bind(&mut self, child_id: usize, envid: usize) {
        debugln!("> POOL: forked pools from {} to {}...", envid, child_id);
        match self.envs.get_mut(&envid) {
            None => (),
            Some(v) => {
                let v = v.clone();
                for pool in v.iter() {
                    self.pools.get_mut(pool).unwrap().reference += 1;
                    self.pools.get_mut(pool).unwrap().envs.push(child_id);
                }
                self.envs.insert(child_id, v);
            }
        }
    }

    /// Bind bind an env to a pool. If the pool does not exist or the env has
    /// been bind to the pool, This method will fail.
    pub fn bind(&mut self, poolid: usize, envid: usize) -> Result<&Vec<usize>, KError> {
        match self.pools.get_mut(&poolid) {
            None => Err(KError::PoolNotFound),
            Some(pool) => {
                if pool.envs.contains(&envid) {
                    Err(KError::PoolDoubleBind)
                } else {
                    pool.envs.push(envid);
                    pool.reference += 1;
                    match self.envs.get_mut(&envid) {
                        None => {
                            debugln!("> POOL: new env {}...", envid);
                            let _ = self.envs.insert(envid, vec![poolid]);
                        }
                        Some(v) => v.push(poolid),
                    }
                    Ok(&pool.pages)
                }
            }
        }
    }

    /// Unbind an env from a pool. If unbind successfully, the pool will get a
    /// decrease-reference.
    fn unbind(&mut self, poolid: usize, envid: usize) -> Result<(), KError> {
        match self.pools.get_mut(&poolid) {
            None => Err(KError::PoolNotFound),
            Some(pool) => {
                if pool.deref(envid)? {
                    debugln!("> POOL: pool {} removed", poolid);
                    self.pools.remove(&poolid);
                }
                Ok(())
            }
        }
    }

    /// Unbind all the pools bind to the env. Called by
    /// [env_free](crate::process::envs::env_free).
    pub fn destory_env(&mut self, envid: usize) {
        match self.envs.get_mut(&envid) {
            None => (),
            Some(_) => {
                let v = self.envs.remove(&envid).unwrap();
                for poolid in v {
                    debugln!("> POOL: unbind {} from env {}", poolid, envid);
                    let _ = self.unbind(poolid, envid);
                }
            }
        }
    }

    /// Lock the pool with `poolid`, and the locker will be `envid`.
    pub fn lock(&mut self, poolid: usize, envid: usize) -> Result<bool, KError> {
        match self.pools.get_mut(&poolid) {
            None => Err(KError::PoolNotFound),
            Some(pool) => {
                if !pool.envs.contains(&envid) {
                    Err(KError::PoolNotBind)
                } else {
                    Ok(pool.lock(envid))
                }
            }
        }
    }

    /// Unlock the pool with `poolid`, and the locker will be `envid`.
    pub fn unlock(&mut self, poolid: usize, envid: usize) -> Result<(), KError> {
        match self.pools.get_mut(&poolid) {
            None => Err(KError::PoolNotFound),
            Some(pool) => pool.unlock(envid),
        }
    }
}

/// Global shared memory pool.
pub static MEMORY_POOL: SyncImplRef<MemoryPool> = SyncImplRef::new(MemoryPool::new());

/// Used to spawn the pool id. Increase one-by-one when doing [mkpoolid].
static POOL_I: AtomicUsize = AtomicUsize::new(1);

/// Spawn the unique id of a new pool.
fn mkpoolid(envid: usize) -> usize {
    (POOL_I.fetch_add(1, SeqCst) << (1 + LOG2NENV)) | (envid & (NENV - 1))
}