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
extern crate time;

use std::cmp::PartialEq;
use std::sync::{Mutex, Condvar, PoisonError, MutexGuard, LockResult};

pub enum Notify {
	One,
	All,
}

pub struct ConditionVariable<T> {
	pair: (Mutex<T>, Condvar)
}

impl<T:PartialEq+Clone> ConditionVariable<T> {
	pub fn new(value: T) -> ConditionVariable<T> {
		ConditionVariable {
			pair: (Mutex::new(value), Condvar::new())
		}
	}

	pub fn set(&self, value: T, notify: Notify) {
		let &(ref lock, ref cvar) = &self.pair;

		let mut data = lock.lock().unwrap();
		*data = value;

		match notify {
			Notify::One => cvar.notify_one(),
			Notify::All => cvar.notify_all(),
		}
	}

	pub fn get(&self) -> Result<T, PoisonError<MutexGuard<T>>> {
		let &(ref lock, _) = &self.pair;

		let data = try!(lock.lock());

		Ok(data.clone())
	}

	pub fn wait_for(&self, expected: T) -> Result<(), PoisonError<MutexGuard<T>>> {
		let &(ref lock, ref cvar) = &self.pair;
		let mut actual = try!(lock.lock());
		
		while *actual != expected {
			actual = try!(cvar.wait(actual));
		}

		Ok(())
	}

	pub fn wait_for_ms(&self, expected: T, timeout_ms: i64) -> Result<bool, PoisonError<(MutexGuard<T>,bool)>> {
		let &(ref lock, ref cvar) = &self.pair;
		let mut actual = lock.lock().unwrap();

		let mut remaining_ms = timeout_ms;
		while *actual != expected && remaining_ms > 0 {
			let before_ms = time::precise_time_ns()/1000;

			let (new, _) = try!(cvar.wait_timeout_ms(actual, remaining_ms as u32));
			actual = new;

			let after_ms = time::precise_time_ns()/1000;
			remaining_ms -= (after_ms - before_ms) as i64;
		}

		Ok(*actual == expected)
	}
}

impl ConditionVariable<()> {
	/// waits for a notify (useful if T==())
	pub fn wait_ms(&self, timeout_ms: u32) -> LockResult<(MutexGuard<()>,bool)>
	{
		let &(ref lock, ref cvar) = &self.pair;
		let guard = lock.lock().unwrap();
		
		cvar.wait_timeout_ms(guard, timeout_ms)
	}
}

#[cfg(test)]
mod tests {
	use std::sync::Arc;
	use std::thread::{sleep_ms, spawn};

	use ::Notify;
	use ::ConditionVariable;

	#[test]
	fn test_wait_for() {
		let cvar1 = Arc::new(ConditionVariable::new(false));
		let cvar2 = cvar1.clone();

		spawn(move || {
			cvar2.set(true, Notify::All);
		});

		cvar1.wait_for(true).unwrap();
	}

	#[test]
	fn test_wait_for_ms() {
		let cvar1 = Arc::new(ConditionVariable::new(false));
		let cvar2 = cvar1.clone();

		spawn(move || {
			sleep_ms(500);
			cvar2.set(true, Notify::All);
		});

		assert_eq!(cvar1.wait_for_ms(true, 1000).unwrap(), true);
	}

	#[test]
	fn test_wait_for_ms_fail() {
		let cvar1 = Arc::new(ConditionVariable::new(false));
		let cvar2 = cvar1.clone();

		spawn(move || {
			sleep_ms(1000);
			cvar2.set(true, Notify::All);
		});

		assert_eq!(cvar1.wait_for_ms(true, 500).unwrap(), false);
	}
}