diff options
| author | Ben Blum <bblum@andrew.cmu.edu> | 2012-08-07 18:42:33 -0400 |
|---|---|---|
| committer | Ben Blum <bblum@andrew.cmu.edu> | 2012-08-07 18:57:49 -0400 |
| commit | 64ba7a31cbf2218c8f9e69aa778d1bfe332ab218 (patch) | |
| tree | 6800c3b677016a6bbd30228ed6b2ab1e35b9d5f2 /src/libcore/sync.rs | |
| parent | a3f9e18b7acb64d905db4c76e483f5b555cb9188 (diff) | |
Touch up semaphores; add another test
Diffstat (limited to 'src/libcore/sync.rs')
| -rw-r--r-- | src/libcore/sync.rs | 60 |
1 files changed, 44 insertions, 16 deletions
diff --git a/src/libcore/sync.rs b/src/libcore/sync.rs index 33823729662..3e1b85441e9 100644 --- a/src/libcore/sync.rs +++ b/src/libcore/sync.rs @@ -17,19 +17,23 @@ type signal_end = pipes::chan<()>; type waitqueue = { head: pipes::port<signal_end>, tail: pipes::chan<signal_end> }; -fn new_waiter() -> (signal_end, wait_end) { pipes::stream() } +fn waitqueue() -> waitqueue { + let (tail, head) = pipes::stream(); + { head: head, tail: tail } +} -/// A counting semaphore. -enum semaphore = exclusive<{ +/// A counting, blocking, bounded-waiting semaphore. +enum semaphore = exclusive<semaphore_inner>; +type semaphore_inner = { mut count: int, waiters: waitqueue, -}>; + //blocked: waitqueue, +}; /// Create a new semaphore with the specified count. fn new_semaphore(count: int) -> semaphore { - let (tail, head) = pipes::stream(); semaphore(exclusive({ mut count: count, - waiters: { head: head, tail: tail } })) + waiters: waitqueue(), /* blocked: waitqueue() */ })) } impl semaphore for &semaphore { @@ -42,20 +46,21 @@ impl semaphore for &semaphore { * Acquires a resource represented by the semaphore. Blocks if necessary * until resource(s) become available. */ - fn wait() { + fn acquire() { let mut waiter_nobe = none; unsafe { do (**self).with |state| { state.count -= 1; if state.count < 0 { - let (signal_end,wait_end) = new_waiter(); + let (signal_end,wait_end) = pipes::stream(); waiter_nobe = some(wait_end); // Enqueue ourself. state.waiters.tail.send(signal_end); } } } - for 1000.times { task::yield(); } + // Uncomment if you wish to test for sem races. Not valgrind-friendly. + /* for 1000.times { task::yield(); } */ // Need to wait outside the exclusive. if waiter_nobe.is_some() { let _ = option::unwrap(waiter_nobe).recv(); @@ -66,7 +71,7 @@ impl semaphore for &semaphore { * Release a held resource represented by the semaphore. Wakes a blocked * contending task, if any exist. */ - fn signal() { + fn release() { unsafe { do (**self).with |state| { state.count += 1; @@ -85,7 +90,7 @@ impl semaphore for &semaphore { /// Runs a function with ownership of one of the semaphore's resources. fn access<U>(blk: fn() -> U) -> U { - self.wait(); + self.acquire(); let _x = sem_release(self); blk() } @@ -95,7 +100,7 @@ impl semaphore for &semaphore { struct sem_release { sem: &semaphore; new(sem: &semaphore) { self.sem = sem; } - drop { self.sem.signal(); } + drop { self.sem.release(); } } #[cfg(test)] @@ -120,11 +125,11 @@ mod tests { let s = ~new_semaphore(0); let s2 = ~s.clone(); do task::spawn { - s2.wait(); + s2.acquire(); c.send(()); } for 10.times { task::yield(); } - s.signal(); + s.release(); let _ = p.recv(); /* Parent waits and child signals */ @@ -133,14 +138,16 @@ mod tests { let s2 = ~s.clone(); do task::spawn { for 10.times { task::yield(); } - s2.signal(); + s2.release(); let _ = p.recv(); } - s.wait(); + s.acquire(); c.send(()); } #[test] fn test_sem_mutual_exclusion() { + // Unsafely achieve shared state, and do the textbook + // "load tmp <- ptr; inc tmp; store ptr <- tmp" dance. let (c,p) = pipes::stream(); let s = ~new_semaphore(1); let s2 = ~s.clone(); @@ -167,7 +174,28 @@ mod tests { } } #[test] + fn test_sem_multi_resource() { + // Parent and child both get in the critical section at the same + // time, and shake hands. + let s = ~new_semaphore(2); + let s2 = ~s.clone(); + let (c1,p1) = pipes::stream(); + let (c2,p2) = pipes::stream(); + do task::spawn { + do s2.access { + let _ = p2.recv(); + c1.send(()); + } + } + do s.access { + c2.send(()); + let _ = p1.recv(); + } + } + #[test] fn test_sem_runtime_friendly_blocking() { + // Force the runtime to schedule two threads on the same sched_loop. + // When one blocks, it should schedule the other one. do task::spawn_sched(task::manual_threads(1)) { let s = ~new_semaphore(1); let s2 = ~s.clone(); |
