Skip to content

Commit 342dc21

Browse files
committed
Disconnect ports before draining them. Issue #1155
1 parent 809ca13 commit 342dc21

File tree

6 files changed

+55
-2
lines changed

6 files changed

+55
-2
lines changed

src/lib/comm.rs

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ native mod rustrt {
4545

4646
fn new_port(unit_sz: uint) -> *rust_port;
4747
fn del_port(po: *rust_port);
48+
fn rust_port_detach(po: *rust_port);
4849
fn get_port_id(po: *rust_port) -> port_id;
4950
fn rust_port_size(po: *rust_port) -> ctypes::size_t;
5051
}
@@ -79,6 +80,9 @@ tag chan<uniq T> {
7980
}
8081

8182
resource port_ptr<uniq T>(po: *rustrt::rust_port) {
83+
// Once the port is detached it's guaranteed not to receive further
84+
// messages
85+
rustrt::rust_port_detach(po);
8286
// Drain the port so that all the still-enqueued items get dropped
8387
while rustrt::rust_port_size(po) > 0u {
8488
// FIXME: For some reason if we don't assign to something here

src/rt/rust_builtin.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,24 @@ new_port(size_t unit_sz) {
467467
return new (task->kernel, "rust_port") rust_port(task, unit_sz);
468468
}
469469

470+
extern "C" CDECL void
471+
rust_port_detach(rust_port *port) {
472+
rust_task *task = rust_scheduler::get_task();
473+
LOG(task, comm, "rust_port_detach(0x%" PRIxPTR ")", (uintptr_t) port);
474+
port->detach();
475+
// FIXME: Busy waiting until we're the only ref
476+
bool done = false;
477+
while (!done) {
478+
scoped_lock with(port->lock);
479+
done = port->ref_count == 1;
480+
}
481+
}
482+
470483
extern "C" CDECL void
471484
del_port(rust_port *port) {
472485
rust_task *task = rust_scheduler::get_task();
473486
LOG(task, comm, "del_port(0x%" PRIxPTR ")", (uintptr_t) port);
474-
scoped_lock with(task->lock);
487+
A(task->sched, port->ref_count == 1, "Expected port ref_count == 1");
475488
port->deref();
476489
}
477490

src/rt/rust_port.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@ rust_port::rust_port(rust_task *task, size_t unit_sz)
1717
rust_port::~rust_port() {
1818
LOG(task, comm, "~rust_port 0x%" PRIxPTR, (uintptr_t) this);
1919

20-
task->release_port(id);
2120
task->deref();
2221
}
2322

23+
void rust_port::detach() {
24+
I(task->sched, !task->lock.lock_held_by_current_thread());
25+
scoped_lock with(task->lock);
26+
{
27+
task->release_port(id);
28+
}
29+
}
30+
2431
void rust_port::send(void *sptr) {
2532
I(task->sched, !lock.lock_held_by_current_thread());
2633
scoped_lock with(lock);

src/rt/rust_port.h

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class rust_port : public kernel_owned<rust_port>, public rust_cond {
2020
void send(void *sptr);
2121
bool receive(void *dptr);
2222
size_t size();
23+
void detach();
2324
};
2425

2526
//

src/rt/rustrt.def.in

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ rust_get_stdout
3434
rust_get_stderr
3535
rust_str_push
3636
rust_list_files
37+
rust_port_detach
3738
rust_port_size
3839
rust_process_wait
3940
rust_ptr_eq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std;
2+
import std::int;
3+
import std::comm;
4+
import std::task;
5+
6+
// We're trying to trigger a race between send and port destruction that
7+
// results in the string not being freed
8+
9+
fn starship(&&ch: std::comm::chan<str>) {
10+
int::range(0, 10) { |_i|
11+
comm::send(ch, "pew pew");
12+
}
13+
}
14+
15+
fn starbase(&&_args: ()) {
16+
int::range(0, 10) { |_i|
17+
let p = comm::port();
18+
task::spawn(comm::chan(p), starship);
19+
task::yield();
20+
}
21+
}
22+
23+
fn main() {
24+
int::range(0, 10) { |_i|
25+
task::spawn((), starbase);
26+
}
27+
}

0 commit comments

Comments
 (0)