From e394ebda37bf6bbe4c516e2b9381aac8bd964dcc Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 6 Jun 2012 15:06:24 -0700 Subject: [PATCH] Adding a lock/condition variable to libcore. --- mk/rt.mk | 1 + src/libcore/sys.rs | 72 +++++++++++++++++++++++++++++++++++++++ src/rt/rust_builtin.cpp | 55 ++++++++++++++++++++++++++++++ src/rt/rust_cond_lock.cpp | 6 ++++ src/rt/rust_cond_lock.h | 15 ++++++++ src/rt/rustrt.def.in | 6 ++++ 6 files changed, 155 insertions(+) create mode 100644 src/rt/rust_cond_lock.cpp create mode 100644 src/rt/rust_cond_lock.h diff --git a/mk/rt.mk b/mk/rt.mk index 02fcdc0def6..aebef1f54f9 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -70,6 +70,7 @@ RUNTIME_CS_$(1) := \ rt/rust_cc.cpp \ rt/rust_debug.cpp \ rt/rust_box_annihilator.cpp \ + rt/rust_cond_lock.cpp \ rt/memory_region.cpp \ rt/boxed_region.cpp \ rt/arch/$$(HOST_$(1))/context.cpp \ diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index e0fd21f6c4a..90ea10601ae 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -7,6 +7,7 @@ export min_align_of; export pref_align_of; export refcount; export log_str; +export lock_and_signal, condition, methods; enum type_desc = { first_param: **libc::c_int, @@ -15,11 +16,20 @@ enum type_desc = { // Remaining fields not listed }; +type rust_cond_lock = *libc::c_void; + #[abi = "cdecl"] native mod rustrt { pure fn refcount(t: *()) -> libc::intptr_t; fn unsupervise(); pure fn shape_log_str(t: *sys::type_desc, data: *()) -> str; + + fn rust_create_cond_lock() -> rust_cond_lock; + fn rust_destroy_cond_lock(lock: rust_cond_lock); + fn rust_lock_cond_lock(lock: rust_cond_lock); + fn rust_unlock_cond_lock(lock: rust_cond_lock); + fn rust_wait_cond_lock(lock: rust_cond_lock); + fn rust_signal_cond_lock(lock: rust_cond_lock) -> bool; } #[abi = "rust-intrinsic"] @@ -74,8 +84,50 @@ pure fn log_str(t: T) -> str { } } +resource lock_and_signal(lock: rust_cond_lock) { + rustrt::rust_destroy_cond_lock(lock); +} + +enum condition { + condition_(rust_cond_lock) +} + +resource unlock(lock: rust_cond_lock) { + rustrt::rust_unlock_cond_lock(lock); +} + +fn create_lock() -> lock_and_signal { + lock_and_signal(rustrt::rust_create_cond_lock()) +} + +impl methods for lock_and_signal { + fn lock(f: fn() -> T) -> T { + rustrt::rust_lock_cond_lock(*self); + let _r = unlock(*self); + f() + } + + fn lock_cond(f: fn(condition) -> T) -> T { + rustrt::rust_lock_cond_lock(*self); + let _r = unlock(*self); + f(condition_(*self)) + } +} + +impl methods for condition { + fn wait() { + rustrt::rust_wait_cond_lock(*self); + } + + fn signal() -> bool { + rustrt::rust_signal_cond_lock(*self) + } +} + #[cfg(test)] mod tests { + use std; + import std::arc; #[test] fn size_of_basic() { @@ -121,6 +173,26 @@ mod tests { assert pref_align_of::() == 8u; assert pref_align_of::<*uint>() == 8u; } + + #[test] + fn condition_variable() { + let lock = arc::arc(create_lock()); + let lock2 = arc::clone(&lock); + + task::spawn {|move lock2| + let lock = arc::get(&lock2); + (*lock).lock_cond {|c| + c.wait(); + } + } + + let mut signaled = false; + while !signaled { + (*arc::get(&lock)).lock_cond {|c| + signaled = c.signal() + } + } + } } // Local Variables: diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 2a65012b52a..25da500cb23 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -7,6 +7,7 @@ #include "sync/timer.h" #include "rust_abi.h" #include "rust_port.h" +#include "rust_cond_lock.h" #include @@ -862,6 +863,60 @@ rust_task_allow_kill() { task->allow_kill(); } +extern "C" rust_cond_lock* +rust_create_cond_lock() { + return new rust_cond_lock(); +} + +extern "C" void +rust_destroy_cond_lock(rust_cond_lock *lock) { + delete lock; +} + +extern "C" void +rust_lock_cond_lock(rust_cond_lock *lock) { + lock->lock.lock(); +} + +extern "C" void +rust_unlock_cond_lock(rust_cond_lock *lock) { + lock->lock.unlock(); +} + +// The next two functions do not use the built in condition variable features +// because the Rust schedule is not aware of them, and they can block the +// scheduler thread. + +extern "C" void +rust_wait_cond_lock(rust_cond_lock *lock) { + rust_task *task = rust_get_current_task(); +#ifdef DEBUG_LOCKS + assert(lock->lock.lock_held_by_current_thread()); +#endif + assert(NULL == lock->waiting); + lock->waiting = task; + lock->lock.unlock(); + task->block(lock, "waiting for signal"); + lock->lock.lock(); + lock->waiting = NULL; +} + +extern "C" bool +rust_signal_cond_lock(rust_cond_lock *lock) { +#ifdef DEBUG_LOCKS + assert(lock->lock.lock_held_by_current_thread()); +#endif + if(NULL == lock->waiting) { + return false; + } + else { + lock->waiting->wakeup(lock); + return true; + } +} + + + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_cond_lock.cpp b/src/rt/rust_cond_lock.cpp new file mode 100644 index 00000000000..cdeccfdc26c --- /dev/null +++ b/src/rt/rust_cond_lock.cpp @@ -0,0 +1,6 @@ +#include "rust_cond_lock.h" + +rust_cond_lock::rust_cond_lock() + : waiting(NULL) +{ +} diff --git a/src/rt/rust_cond_lock.h b/src/rt/rust_cond_lock.h new file mode 100644 index 00000000000..0adafd59678 --- /dev/null +++ b/src/rt/rust_cond_lock.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// A lock and condition variable pair that is useable from Rust. + +#pragma once + +#include "sync/lock_and_signal.h" +#include "rust_globals.h" +#include "rust_task.h" + +struct rust_cond_lock : public rust_cond { + rust_cond_lock(); + + lock_and_signal lock; + rust_task *waiting; +}; diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 7977c42cfa4..5cf6ee61773 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -163,3 +163,9 @@ rust_port_drop rust_port_task rust_task_inhibit_kill rust_task_allow_kill +rust_create_cond_lock +rust_destroy_cond_lock +rust_lock_cond_lock +rust_unlock_cond_lock +rust_wait_cond_lock +rust_signal_cond_lock