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
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};

/// A pool that contains elements that can be recycled when they are finished being used.
#[derive(Clone)]
pub struct PoisonPool<T> {
    all: Rc<Vec<RefCell<(bool, Option<T>)>>>
}

/// An element that logically came from a PoisonPool.
///
/// If this item came from `PoisonPool::get*()`, then when this Item is
/// Dropped, it will be recycled into the pool.
///
/// If this item came from the `from_value` constructor, then when this Item is
/// Dropped, the contained value will be dropped as well.
pub struct Item<T> {
    /// Maybe have this be a weak reference instead?
    parent_pool: Option<PoisonPool<T>>,
    idx: usize,
    item: Option<T>,
    poisoned: bool
}

impl <T> PoisonPool<T> {
    /// Creates a new PoisonPool with a given size and where each element is
    /// initialized by the init function.
    pub fn new<F: FnMut() -> T>(count: usize, mut init: F) -> PoisonPool<T> {
        let mut v = Vec::new();
        v.extend((0 .. count).map(|_| RefCell::new((false, Some(init())))));
        PoisonPool { all: Rc::new(v) }
    }

    /// Returns an unpoisoned item from the pool if possible.
    pub fn get(&self) -> Option<Item<T>> {
        for (i, slot) in self.all.iter().enumerate() {
            if !slot.borrow().0 && slot.borrow().1.is_some() {
                return Some(Item {
                    parent_pool: Some(PoisonPool{all: self.all.clone()}),
                    idx: i,
                    item: slot.borrow_mut().1.take(),
                    poisoned: false
                })
            }
        }

        None
    }

    /// Returns an unpoisoned item from the pool, or a given value, if one
    /// is not available.
    pub fn get_or(&self, v: T) -> Item<T> {
        self.get().unwrap_or(Item::from_value(v))
    }

    /// Returns an unpoisoned item from the pool, or value generated by the function
    /// `f` if one is not available
    pub fn get_or_else<F: FnOnce() -> T>(&self, f: F) -> Item<T> {
        self.get().unwrap_or_else(|| {
            Item::from_value(f())
        })
    }

    /// Removes poison from all the internal items
    pub fn unpoison_all(&self) {
        for slot in self.all.iter() {
            *(&mut slot.borrow_mut().0) = false;
        }
    }
}

impl <T> Item<T> {
    /// Directly construct an Item.  When this Item is dropped, it will
    /// not be returned to any Pool.
    pub fn from_value(value: T) -> Item<T> {
        Item {
            parent_pool: None,
            idx: 0,
            item: Some(value),
            poisoned: false
        }
    }

    /// Replaces the value of the item with another one.
    pub fn replace(&mut self, new: T) -> T {
        let old = self.item.take().unwrap();
        self.item = Some(new);
        old
    }

    /// Poisons this value.
    ///
    /// Even when this item is returned to the pool, this item will not be
    /// used until `unpoison_all` is called on the owning pool.
    pub fn poison(mut self) {
        self.poisoned = true;
    }
}

impl <T> Deref for Item<T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.item.as_ref().unwrap()
    }
}

impl <T> DerefMut for Item<T> {
    fn deref_mut(&mut self) -> &mut T {
        self.item.as_mut().unwrap()
    }
}

impl <T> Drop for Item<T> {
    fn drop(&mut self) {
        let it = self.item.take();
        if let Some(pc) = self.parent_pool.take() {
            *(pc.all.get(self.idx).unwrap().borrow_mut()) = (self.poisoned, it);
        }
    }
}

#[test]
fn test_empty() {
    let rc = PoisonPool::new(0, || 0u32);
    assert!(rc.get().is_none())
}

#[test]
fn test_single() {
    let rc = PoisonPool::new(1, || 5u32);
    assert!(&*rc.get().unwrap() == &5u32)
}

#[test]
fn test_reuse() {
    let rc = PoisonPool::new(1, || 5u32);

    {
        let mut it = rc.get().unwrap();
        *it = 10u32;
    }

    {
        let it = rc.get().unwrap();
        assert!(&*it == &10u32)
    }
}

#[test]
fn test_taken() {
    let rc = PoisonPool::new(1, || 5u32);
    let it1 = rc.get();
    assert!(it1.is_some());
    let it2 = rc.get();
    assert!(it2.is_none());
}

#[test]
fn test_replace() {
    let rc = PoisonPool::new(1, || 5u32);
    {
        let mut it = rc.get().unwrap();
        assert!(it.replace(4) == 5);
    }

    {
        let it = rc.get().unwrap();
        assert!(*it == 4)
    }
}

#[test]
fn test_poison() {
    let rc = PoisonPool::new(1, || 5u32);

    {
        let mut it = rc.get().unwrap();
        *it = 10u32;
        it.poison()
    }

    {
        assert!(rc.get().is_none());
    }
}

#[test]
fn test_unpoison() {
    let rc = PoisonPool::new(1, || 5u32);

    {
        let mut it = rc.get().unwrap();
        *it = 10u32;
        it.poison()
    }

    rc.unpoison_all();

    {
        assert!(rc.get().is_some());
    }
}