blob: 701bf02ef96a8702706f918a7d1fd483820c93c8 [file] [log] [blame]
// Copyright 2015 Colin Sherratt
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use {Signal, ArmedSignal, Pulse, Waiting, Barrier, Signals};
pub struct Inner {
pub ready: Vec<usize>,
pub trigger: Option<Pulse>,
}
pub struct Handle(pub Arc<Mutex<Inner>>);
/// A `Select` listens to 1 or more signals. It will wait until
/// any signal becomes available before Pulsing. `Select` will then
/// return the `Signal` that has been `Pulsed`. `Select` has no defined
/// ordering of events for `Signal`s when there are more then one `Signals`
/// pending.
pub struct Select {
inner: Arc<Mutex<Inner>>,
signals: HashMap<usize, ArmedSignal>,
}
impl Select {
/// Create a new empty `Select`
pub fn new() -> Select {
Select {
inner: Arc::new(Mutex::new(Inner {
ready: Vec::new(),
trigger: None,
})),
signals: HashMap::new(),
}
}
/// Add a signal to the `Select`, a unique id that is associated
/// With the signal is returned. This can be used to remove the
/// signal from the `Select` or to lookup the `Pulse` when it fires.
pub fn add(&mut self, pulse: Signal) -> usize {
let id = pulse.id();
let p = pulse.arm(Waiting::select(Handle(self.inner.clone())));
self.signals.insert(id, p);
id
}
/// Remove a `Signal1 from the `Select` using it's unique id.
pub fn remove(&mut self, id: usize) -> Option<Signal> {
self.signals
.remove(&id)
.map(|x| x.disarm())
}
/// Convert all the signals present in the `Select` into a `Barrier`
pub fn into_barrier(self) -> Barrier {
let vec: Vec<Signal> = self.signals
.into_iter()
.map(|(_, p)| p.disarm())
.collect();
Barrier::new(&vec)
}
/// This is a non-blocking attempt to get a `Signal` from a `Select`
/// this will return a `Some(Signal)` if there is a pending `Signal`
/// in the select. Otherwise it will return `None`
pub fn try_next(&mut self) -> Option<Signal> {
let mut guard = self.inner.lock().unwrap();
if let Some(x) = guard.ready.pop() {
return Some(self.signals.remove(&x).map(|x| x.disarm()).unwrap());
}
None
}
/// Get the number of Signals being watched
pub fn len(&self) -> usize {
self.signals.len()
}
}
impl Iterator for Select {
type Item = Signal;
fn next(&mut self) -> Option<Signal> {
loop {
if self.signals.len() == 0 {
return None;
}
let pulse = {
let mut guard = self.inner.lock().unwrap();
while let Some(x) = guard.ready.pop() {
if let Some(x) = self.signals.remove(&x) {
return Some(x.disarm());
}
}
let (pulse, t) = Signal::new();
guard.trigger = Some(t);
pulse
};
pulse.wait().unwrap();
}
}
}
impl Signals for Select {
fn signal(&self) -> Signal {
let (pulse, t) = Signal::new();
let mut guard = self.inner.lock().unwrap();
if guard.ready.len() == 0 {
guard.trigger = Some(t);
} else {
t.pulse();
}
pulse
}
}
/// `SelectMap` is a wrapper around a `Select` rather then use
/// a unique id to find out what signal has been asserts, `SelectMap`
/// will return an supplied object.
pub struct SelectMap<T> {
select: Select,
items: HashMap<usize, T>,
}
impl<T> SelectMap<T> {
/// Create a new empty `SelectMap`
pub fn new() -> SelectMap<T> {
SelectMap {
select: Select::new(),
items: HashMap::new(),
}
}
/// Add a `Signal` and an associated value into the `SelectMap`
pub fn add(&mut self, signal: Signal, value: T) {
let id = self.select.add(signal);
self.items.insert(id, value);
}
/// This is a non-blocking attempt to get a `Signal` from a `SelectMap`
/// this will return a `Some((Signal, T))` if there is a pending `Signal`
/// in the select. Otherwise it will return `None`
pub fn try_next(&mut self) -> Option<(Signal, T)> {
self.select.try_next().map(|x| {
let id = x.id();
(x, self.items.remove(&id).unwrap())
})
}
/// Get the number of items in the `SelectMap`
pub fn len(&self) -> usize {
self.items.len()
}
}
impl<T> Iterator for SelectMap<T> {
type Item = (Signal, T);
fn next(&mut self) -> Option<(Signal, T)> {
self.select.next().map(|x| {
let id = x.id();
(x, self.items.remove(&id).unwrap())
})
}
}
impl<T> Signals for SelectMap<T> {
fn signal(&self) -> Signal {
self.select.signal()
}
}