Skip to content

Commit ae094a7

Browse files
committed
Add 'do atomically { .. }' for exclusives
1 parent 9103e43 commit ae094a7

File tree

6 files changed

+91
-13
lines changed

6 files changed

+91
-13
lines changed

src/libcore/sys.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export refcount;
99
export log_str;
1010
export lock_and_signal, condition, methods;
1111

12+
import task::atomically;
13+
1214
enum type_desc = {
1315
size: uint,
1416
align: uint
@@ -105,13 +107,17 @@ impl methods for lock_and_signal {
105107
unsafe fn lock<T>(f: fn() -> T) -> T {
106108
rustrt::rust_lock_cond_lock(self.lock);
107109
let _r = unlock(self.lock);
108-
f()
110+
do atomically {
111+
f()
112+
}
109113
}
110114

111115
unsafe fn lock_cond<T>(f: fn(condition) -> T) -> T {
112116
rustrt::rust_lock_cond_lock(self.lock);
113117
let _r = unlock(self.lock);
114-
f(condition_(self.lock))
118+
do atomically {
119+
f(condition_(self.lock))
120+
}
115121
}
116122
}
117123

src/libcore/task.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export yield;
6060
export failing;
6161
export get_task;
6262
export unkillable;
63+
export atomically;
6364

6465
export local_data_key;
6566
export local_data_pop;
@@ -683,16 +684,36 @@ fn get_task() -> task {
683684
*/
684685
unsafe fn unkillable(f: fn()) {
685686
class allow_failure {
686-
let i: (); // since a class must have at least one field
687-
new(_i: ()) { self.i = (); }
688-
drop { rustrt::rust_task_allow_kill(); }
687+
let t: *rust_task;
688+
new(t: *rust_task) { self.t = t; }
689+
drop { rustrt::rust_task_allow_kill(self.t); }
689690
}
690691

691-
let _allow_failure = allow_failure(());
692-
rustrt::rust_task_inhibit_kill();
692+
let t = rustrt::rust_get_task();
693+
let _allow_failure = allow_failure(t);
694+
rustrt::rust_task_inhibit_kill(t);
693695
f();
694696
}
695697

698+
/**
699+
* A stronger version of unkillable that also inhibits scheduling operations.
700+
* For use with exclusive ARCs, which use pthread mutexes directly.
701+
*/
702+
unsafe fn atomically<U>(f: fn() -> U) -> U {
703+
class defer_interrupts {
704+
let t: *rust_task;
705+
new(t: *rust_task) { self.t = t; }
706+
drop {
707+
rustrt::rust_task_allow_yield(self.t);
708+
rustrt::rust_task_allow_kill(self.t);
709+
}
710+
}
711+
let t = rustrt::rust_get_task();
712+
let _interrupts = defer_interrupts(t);
713+
rustrt::rust_task_inhibit_kill(t);
714+
rustrt::rust_task_inhibit_yield(t);
715+
f()
716+
}
696717

697718
/****************************************************************************
698719
* Internal
@@ -1235,8 +1256,10 @@ extern mod rustrt {
12351256

12361257
fn rust_task_is_unwinding(task: *rust_task) -> bool;
12371258
fn rust_osmain_sched_id() -> sched_id;
1238-
fn rust_task_inhibit_kill();
1239-
fn rust_task_allow_kill();
1259+
fn rust_task_inhibit_kill(t: *rust_task);
1260+
fn rust_task_allow_kill(t: *rust_task);
1261+
fn rust_task_inhibit_yield(t: *rust_task);
1262+
fn rust_task_allow_yield(t: *rust_task);
12401263
fn rust_task_kill_other(task: *rust_task);
12411264
fn rust_task_kill_all(task: *rust_task);
12421265

@@ -1759,6 +1782,21 @@ fn test_unkillable_nested() {
17591782
po.recv();
17601783
}
17611784
1785+
#[test] #[should_fail] #[ignore(cfg(windows))]
1786+
fn test_atomically() {
1787+
unsafe { do atomically { yield(); } }
1788+
}
1789+
1790+
#[test]
1791+
fn test_atomically2() {
1792+
unsafe { do atomically { } } yield(); // shouldn't fail
1793+
}
1794+
1795+
#[test] #[should_fail] #[ignore(cfg(windows))]
1796+
fn test_atomically_nested() {
1797+
unsafe { do atomically { do atomically { } yield(); } }
1798+
}
1799+
17621800
#[test]
17631801
fn test_child_doesnt_ref_parent() {
17641802
// If the child refcounts the parent task, this will stack overflow when

src/rt/rust_builtin.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -854,17 +854,25 @@ rust_global_env_chan_ptr() {
854854
}
855855

856856
extern "C" void
857-
rust_task_inhibit_kill() {
858-
rust_task *task = rust_get_current_task();
857+
rust_task_inhibit_kill(rust_task *task) {
859858
task->inhibit_kill();
860859
}
861860

862861
extern "C" void
863-
rust_task_allow_kill() {
864-
rust_task *task = rust_get_current_task();
862+
rust_task_allow_kill(rust_task *task) {
865863
task->allow_kill();
866864
}
867865

866+
extern "C" void
867+
rust_task_inhibit_yield(rust_task *task) {
868+
task->inhibit_yield();
869+
}
870+
871+
extern "C" void
872+
rust_task_allow_yield(rust_task *task) {
873+
task->allow_yield();
874+
}
875+
868876
extern "C" void
869877
rust_task_kill_other(rust_task *task) { /* Used for linked failure */
870878
task->kill();

src/rt/rust_task.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
3939
killed(false),
4040
reentered_rust_stack(false),
4141
disallow_kill(0),
42+
disallow_yield(0),
4243
c_stack(NULL),
4344
next_c_sp(0),
4445
next_rust_sp(0)
@@ -234,9 +235,18 @@ rust_task::must_fail_from_being_killed_inner() {
234235
return killed && !reentered_rust_stack && disallow_kill == 0;
235236
}
236237

238+
void rust_task_yield_fail(rust_task *task) {
239+
LOG_ERR(task, task, "task %" PRIxPTR " yielded in an atomic section",
240+
task);
241+
task->fail();
242+
}
243+
237244
// Only run this on the rust stack
238245
void
239246
rust_task::yield(bool *killed) {
247+
if (disallow_yield > 0) {
248+
call_on_c_stack(this, (void *)rust_task_yield_fail);
249+
}
240250
// FIXME (#2875): clean this up
241251
if (must_fail_from_being_killed()) {
242252
{
@@ -672,6 +682,17 @@ rust_task::allow_kill() {
672682
disallow_kill--;
673683
}
674684

685+
void rust_task::inhibit_yield() {
686+
scoped_lock with(lifecycle_lock);
687+
disallow_yield++;
688+
}
689+
690+
void rust_task::allow_yield() {
691+
scoped_lock with(lifecycle_lock);
692+
assert(disallow_yield > 0 && "Illegal allow_yield(): already yieldable!");
693+
disallow_yield--;
694+
}
695+
675696
void *
676697
rust_task::wait_event(bool *killed) {
677698
scoped_lock with(lifecycle_lock);

src/rt/rust_task.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ rust_task : public kernel_owned<rust_task>
185185
// Indicates that we've called back into Rust from C
186186
bool reentered_rust_stack;
187187
unsigned long disallow_kill;
188+
unsigned long disallow_yield;
188189

189190
// The stack used for running C code, borrowed from the scheduler thread
190191
stk_seg *c_stack;
@@ -318,6 +319,8 @@ rust_task : public kernel_owned<rust_task>
318319

319320
void inhibit_kill();
320321
void allow_kill();
322+
void inhibit_yield();
323+
void allow_yield();
321324
};
322325

323326
// FIXME (#2697): It would be really nice to be able to get rid of this.

src/rt/rustrt.def.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ rust_port_drop
183183
rust_port_task
184184
rust_task_inhibit_kill
185185
rust_task_allow_kill
186+
rust_task_inhibit_yield
187+
rust_task_allow_yield
186188
rust_task_kill_other
187189
rust_task_kill_all
188190
rust_create_cond_lock

0 commit comments

Comments
 (0)