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
//! The ArcMap exists to enable multiple Mutex based Elements in a Map to be accessible
//!
//! without the need for locking the whole HashMap, while you access one element.
//!
//! Instead the Map is hidden inside a thread, that will return accesible elements as requested.
//!
//! Rather than limit the access within the fn it seemed simple to return the Arc directly.
//!
//! Though because the Map may not contain the required element,
//! and becuase of thread send/recieve. The get method returns a "Result<Arc<Mutex<G>>,AMapErr>"
//!
//! This can be accessed as follows : *(Though normally for more complex objects)*
//!
//! ```
//! use arc_map::ArcMap;
//! let mut am = ArcMap::new();
//!
//! //update by grabbing mutex  
//! am.insert(3,"hello".to_string());
//! {
//!     let p = am.get(3).unwrap(); //p is Arc<Mutex<String>>
//!     let mut s = p.lock().unwrap();
//!     s.push_str(" world");
//! }
//!
//! //read by grabbing mutex 
//! { 
//!     let p2 = am.get(3).unwrap();
//!     let s2 = p2.lock().unwrap();
//!     assert_eq!(*s2,"hello world".to_string());
//! }
//! 
//! 
//! am.insert(4,"goodbye".to_string());
//!
//! //update in place (No need for scoping)
//! am.on_do(4,|mut s| (&mut s).push_str(" cruel world")).unwrap();
//!
//! //get info out
//! let ls = am.on_do(4,|s| s.clone()).unwrap();
//! 
//! assert_eq!(&ls,"goodbye cruel world");
//!
//! ```
//!
//! While this can be achieved using traditional mutex locks,
//! the interface here is much simpler to use.

use std::collections::HashMap;
use std::sync::mpsc::{channel,Sender,sync_channel,SyncSender};
use std::sync::{Arc,Mutex};
use std::thread;
use std::hash::Hash;
use std::fmt::Debug;

pub mod amap_error;
pub use amap_error::AMapErr;

pub trait MKey : 'static +Send + Eq + Hash{}
impl<K> MKey for K where K:'static+Send+Eq+Hash{}

pub trait MVal: 'static +Send {}
impl <V> MVal for V where V:'static+ Send + Debug{}



/// internal type for sending across job channel
enum Job<K:MKey,V:MVal> {
    Add(K,V,Sender<bool>),
    Get(K,Sender<Option< Arc<  Mutex<V>  > >>),
    Remove(K),
}


#[derive(Clone)]
pub struct ArcMap<K:MKey,V:MVal>{
    ch:SyncSender<Job<K,V>>,
}

fn hide_map<K:MKey,V:MVal>(bsize:usize)->SyncSender<Job<K,V>>{
    let (tx,rx) = sync_channel(bsize);
    thread::spawn(move ||{
        let mut mp:HashMap<K,Arc<Mutex<V>>> = HashMap::new();
        loop {
            match rx.recv(){
                Ok(Job::Add(k,v,cbak))=>{
                    mp.insert(k,Arc::new(Mutex::new(v)));
                    cbak.send(true).unwrap_or(());

                }
                Ok(Job::Get(ref k,ref cbak))=>{
                    match mp.get(k) {
                        Some(ref a)=>cbak.send(Some((*a).clone())).unwrap_or(()),
                        None=>cbak.send(None).unwrap_or(()),
                    }
                }
                Ok(Job::Remove(ref k))=>{
                    mp.remove(k);
                }
                //If all senders are dropped, kill the thread
                _=>return, 
            }
        }
    });
    tx 
}

impl<K:MKey,V:MVal> ArcMap<K,V>{
    /// Create a new ArcMap This will spawn a guard process
    /// That process will die when the last Clone of this map is dropped
    pub fn new()->ArcMap<K,V>{
        ArcMap{
            ch:hide_map(20),//reasonable default
        }
    }

    pub fn new_sized(bsize:usize)->ArcMap<K,V>{
        ArcMap{
            ch:hide_map(bsize),
        }
    }

    
    /// Add a new item to the map. 
    pub fn insert(&self, k:K,v:V)->Result<bool,AMapErr>{
        let (tbak,rbak) = channel();
        self.ch.send(Job::Add(k,v,tbak))?;
        Ok(rbak.recv()?)
    }

    /// The basic way of getting an item out of the list for editing
    /// returns type of (wrapped) Arc means you can keep this after closing and still be safe
    /// In general prefer on_do
    pub fn get(&self, k:K)->Result<Arc<Mutex<V>>,AMapErr>{
        let (tbak,rbak) = channel();
        self.ch.send(Job::Get(k,tbak))?;
        match rbak.recv() {
            Ok(Some(a))=>Ok(a),
            Ok(None)=>Err(AMapErr::NotFound),
            Err(d)=>Err(d.into()),
        }
    }

    /// This function only removes the object from the list.
    /// If you have an Arc Copy that will still be valid
    pub fn remove(&self, k:K)->Result<(),AMapErr>{
        self.ch.send(Job::Remove(k))?;
        Ok(())
    }

    /// Run f on the item at the index "on",
    /// Returns the result of f wrapped in a Result
    /// Allows for reading data out of the object
    /// Errors if index not found, or channel/locking errors
    pub fn on_do<RT,F>(&mut self, on:K,mut f:F)->Result<RT,AMapErr>
        where F:FnMut(&mut V)->RT
    {
        let p = self.get(on)?; 
        let mut v = p.lock()?;
        Ok(f(&mut v))
    }
}



#[cfg(test)]
mod tests{
    use super::*;
    #[test]
    fn add_to_ten(){
        let mut mp = ArcMap::new();

        for i in 0..10 {
            mp.insert(i,0).unwrap();
        }

        let mut handlers = Vec::new();
        for i in 0 .. 10{
            for _ in 0 .. 10 {
                let n = i;
                let m2 = mp.clone();
                let h = thread::spawn(move||{
                    let g = m2.clone().get(n).unwrap();
                    let mut incnum = g.lock().unwrap();
                    *incnum +=1;
                });
                handlers.push(h);
            }
        }

        for i in 0 ..10{
            for _ in 0 ..10{
                let n = i;
                let m2 = mp.clone();
                let h = thread::spawn(move||{
                    m2.clone().on_do(n,|v|*v += 1).unwrap();
                });
                handlers.push(h);
            }
        }

        for h in handlers {
            h.join().unwrap();
        }

        for i in 0..10 {
            mp.on_do(i,|num| assert_eq!(*num,20)).unwrap();
            //same but other method
            let g = mp.get(i).unwrap();
            let num = g.lock().unwrap();
            assert_eq!(*num,20);
        }
    }
}