Skip to content

Commit e43c5bd

Browse files
committed
Rewrite the exchange allocator to work without an active scheduler. #4457
1 parent e91040c commit e43c5bd

14 files changed

+234
-101
lines changed

mk/rt.mk

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ RUNTIME_CXXS_$(1) := \
6363
rt/rust_log.cpp \
6464
rt/rust_gc_metadata.cpp \
6565
rt/rust_util.cpp \
66+
rt/rust_exchange_alloc.cpp \
6667
rt/isaac/randport.cpp \
6768
rt/miniz.cpp \
6869
rt/rust_kernel.cpp \

src/libcore/private.rs

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub mod global;
3535
pub mod finally;
3636
#[path = "private/weak_task.rs"]
3737
pub mod weak_task;
38+
#[path = "private/exchange_alloc.rs"]
39+
pub mod exchange_alloc;
3840

3941
extern mod rustrt {
4042
pub unsafe fn rust_create_little_lock() -> rust_little_lock;
@@ -91,6 +93,17 @@ fn test_run_in_bare_thread() {
9193
}
9294
}
9395

96+
#[test]
97+
fn test_run_in_bare_thread_exchange() {
98+
unsafe {
99+
// Does the exchange heap work without the runtime?
100+
let i = ~100;
101+
do run_in_bare_thread {
102+
assert i == ~100;
103+
}
104+
}
105+
}
106+
94107
fn compare_and_swap(address: &mut int, oldval: int, newval: int) -> bool {
95108
unsafe {
96109
let old = rusti::atomic_cxchg(address, oldval, newval);

src/libcore/private/exchange_alloc.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use sys::{TypeDesc, size_of};
12+
use libc::{c_void, size_t, uintptr_t};
13+
use c_malloc = libc::malloc;
14+
use c_free = libc::free;
15+
use managed::raw::{BoxHeaderRepr, BoxRepr};
16+
use cast::transmute;
17+
use ptr::{set_memory, null};
18+
use intrinsic::TyDesc;
19+
20+
pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void {
21+
unsafe {
22+
assert td.is_not_null();
23+
24+
let total_size = get_box_size(size, (*td).align);
25+
let p = c_malloc(total_size as size_t);
26+
assert p.is_not_null();
27+
28+
// FIXME #4761: Would be very nice to not memset all allocations
29+
let p: *mut u8 = transmute(p);
30+
set_memory(p, 0, total_size);
31+
32+
// FIXME #3475: Converting between our two different tydesc types
33+
let td: *TyDesc = transmute(td);
34+
35+
let box: &mut BoxRepr = transmute(p);
36+
box.header.ref_count = -1; // Exchange values not ref counted
37+
box.header.type_desc = td;
38+
box.header.prev = null();
39+
box.header.next = null();
40+
41+
let exchange_count = &mut *rust_get_exchange_count_ptr();
42+
rusti::atomic_xadd(exchange_count, 1);
43+
44+
return transmute(box);
45+
}
46+
}
47+
48+
pub unsafe fn free(ptr: *c_void) {
49+
let exchange_count = &mut *rust_get_exchange_count_ptr();
50+
rusti::atomic_xsub(exchange_count, 1);
51+
52+
assert ptr.is_not_null();
53+
c_free(ptr);
54+
}
55+
56+
fn get_box_size(body_size: uint, body_align: uint) -> uint {
57+
let header_size = size_of::<BoxHeaderRepr>();
58+
// FIXME (#2699): This alignment calculation is suspicious. Is it right?
59+
let total_size = align_to(header_size, body_align) + body_size;
60+
return total_size;
61+
}
62+
63+
// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
64+
// of two.
65+
fn align_to(size: uint, align: uint) -> uint {
66+
assert align != 0;
67+
(size + align - 1) & !(align - 1)
68+
}
69+
70+
extern {
71+
#[rust_stack]
72+
fn rust_get_exchange_count_ptr() -> *mut int;
73+
}
74+
75+
#[abi = "rust-intrinsic"]
76+
extern mod rusti {
77+
fn atomic_xadd(dst: &mut int, src: int) -> int;
78+
fn atomic_xsub(dst: &mut int, src: int) -> int;
79+
}

src/libcore/rt.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use libc::{c_char, c_uchar, c_void, size_t, uintptr_t};
1818
use managed::raw::BoxRepr;
1919
use str;
2020
use sys;
21+
use private::exchange_alloc;
22+
use cast::transmute;
2123

2224
use gc::{cleanup_stack_for_failure, gc, Word};
2325

@@ -30,13 +32,6 @@ pub const FROZEN_BIT: uint = 0x80000000;
3032
pub const FROZEN_BIT: uint = 0x8000000000000000;
3133

3234
pub extern mod rustrt {
33-
#[rust_stack]
34-
unsafe fn rust_upcall_exchange_malloc(td: *c_char, size: uintptr_t)
35-
-> *c_char;
36-
37-
#[rust_stack]
38-
unsafe fn rust_upcall_exchange_free(ptr: *c_char);
39-
4035
#[rust_stack]
4136
unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char;
4237

@@ -70,10 +65,11 @@ pub unsafe fn rt_fail_borrowed() {
7065
}
7166
}
7267

68+
// XXX: Make these signatures agree with exchange_alloc's signatures
7369
#[rt(exchange_malloc)]
7470
#[lang="exchange_malloc"]
7571
pub unsafe fn rt_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
76-
return rustrt::rust_upcall_exchange_malloc(td, size);
72+
transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
7773
}
7874

7975
// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
@@ -82,7 +78,7 @@ pub unsafe fn rt_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
8278
#[rt(exchange_free)]
8379
#[lang="exchange_free"]
8480
pub unsafe fn rt_exchange_free(ptr: *c_char) {
85-
rustrt::rust_upcall_exchange_free(ptr);
81+
exchange_alloc::free(transmute(ptr))
8682
}
8783

8884
#[rt(malloc)]

src/rt/rust_exchange_alloc.cpp

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#include "rust_exchange_alloc.h"
12+
#include "sync/sync.h"
13+
#include <stdlib.h>
14+
#include <assert.h>
15+
#include <string.h>
16+
#include <stdio.h>
17+
18+
uintptr_t exchange_count = 0;
19+
20+
void *
21+
rust_exchange_alloc::malloc(size_t size, bool zero) {
22+
void *value = ::malloc(size);
23+
assert(value);
24+
if (zero) {
25+
memset(value, 0, size);
26+
}
27+
28+
sync::increment(exchange_count);
29+
30+
return value;
31+
}
32+
33+
void *
34+
rust_exchange_alloc::calloc(size_t size) {
35+
return this->malloc(size);
36+
}
37+
38+
void *
39+
rust_exchange_alloc::realloc(void *ptr, size_t size) {
40+
void *new_ptr = ::realloc(ptr, size);
41+
assert(new_ptr);
42+
return new_ptr;
43+
}
44+
45+
void
46+
rust_exchange_alloc::free(void *ptr) {
47+
sync::decrement(exchange_count);
48+
::free(ptr);
49+
}
50+
51+
extern "C" uintptr_t *
52+
rust_get_exchange_count_ptr() {
53+
return &exchange_count;
54+
}
55+
56+
void
57+
rust_check_exchange_count_on_exit() {
58+
if (exchange_count != 0) {
59+
printf("exchange heap not empty on on exit");
60+
printf("%d dangling allocations", (int)exchange_count);
61+
abort();
62+
}
63+
}

src/rt/rust_exchange_alloc.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#ifndef RUST_EXCHANGE_ALLOC_H
12+
#define RUST_EXCHANGE_ALLOC_H
13+
14+
#include <stddef.h>
15+
#include <stdint.h>
16+
17+
class rust_exchange_alloc {
18+
public:
19+
void *malloc(size_t size, bool zero = true);
20+
void *calloc(size_t size);
21+
void *realloc(void *mem, size_t size);
22+
void free(void *mem);
23+
};
24+
25+
extern "C" uintptr_t *
26+
rust_get_exchange_count_ptr();
27+
28+
void
29+
rust_check_exchange_count_on_exit();
30+
31+
#endif

src/rt/rust_kernel.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
KLOG_LVL(this, field, log_err, __VA_ARGS__)
2323

2424
rust_kernel::rust_kernel(rust_env *env) :
25-
_region(env, true),
2625
_log(NULL),
2726
max_task_id(INIT_TASK_ID-1), // sync_add_and_fetch increments first
2827
rval(0),
@@ -77,21 +76,21 @@ rust_kernel::fatal(char const *fmt, ...) {
7776

7877
void *
7978
rust_kernel::malloc(size_t size, const char *tag) {
80-
return _region.malloc(size, tag);
79+
return exchange_alloc.malloc(size);
8180
}
8281

8382
void *
8483
rust_kernel::calloc(size_t size, const char *tag) {
85-
return _region.calloc(size, tag);
84+
return exchange_alloc.calloc(size);
8685
}
8786

8887
void *
8988
rust_kernel::realloc(void *mem, size_t size) {
90-
return _region.realloc(mem, size);
89+
return exchange_alloc.realloc(mem, size);
9190
}
9291

9392
void rust_kernel::free(void *mem) {
94-
_region.free(mem);
93+
exchange_alloc.free(mem);
9594
}
9695

9796
rust_sched_id
@@ -217,6 +216,7 @@ rust_kernel::run() {
217216
assert(osmain_driver != NULL);
218217
osmain_driver->start_main_loop();
219218
sched_reaper.join();
219+
rust_check_exchange_count_on_exit();
220220
return rval;
221221
}
222222

src/rt/rust_kernel.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@
4545
#include <map>
4646
#include <vector>
4747

48-
#include "memory_region.h"
48+
#include "rust_exchange_alloc.h"
4949
#include "rust_log.h"
5050
#include "rust_sched_reaper.h"
5151
#include "rust_type.h"
5252
#include "util/hash_map.h"
53+
#include "sync/lock_and_signal.h"
5354

5455
class rust_scheduler;
5556
class rust_sched_driver;
@@ -71,7 +72,7 @@ struct exit_functions {
7172
};
7273

7374
class rust_kernel {
74-
memory_region _region;
75+
rust_exchange_alloc exchange_alloc;
7576
rust_log _log;
7677

7778
// The next task id
@@ -135,7 +136,7 @@ class rust_kernel {
135136
void *calloc(size_t size, const char *tag);
136137
void *realloc(void *mem, size_t size);
137138
void free(void *mem);
138-
memory_region *region() { return &_region; }
139+
rust_exchange_alloc *region() { return &exchange_alloc; }
139140

140141
void fail();
141142

src/rt/rust_sched_loop.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ rust_sched_loop::run_single_turn() {
260260

261261
assert(!extra_c_stack);
262262
if (cached_c_stack) {
263-
destroy_stack(kernel->region(), cached_c_stack);
263+
destroy_exchange_stack(kernel->region(), cached_c_stack);
264264
cached_c_stack = NULL;
265265
}
266266

@@ -389,14 +389,15 @@ void
389389
rust_sched_loop::prepare_c_stack(rust_task *task) {
390390
assert(!extra_c_stack);
391391
if (!cached_c_stack && !task->have_c_stack()) {
392-
cached_c_stack = create_stack(kernel->region(), C_STACK_SIZE);
392+
cached_c_stack = create_exchange_stack(kernel->region(),
393+
C_STACK_SIZE);
393394
}
394395
}
395396

396397
void
397398
rust_sched_loop::unprepare_c_stack() {
398399
if (extra_c_stack) {
399-
destroy_stack(kernel->region(), extra_c_stack);
400+
destroy_exchange_stack(kernel->region(), extra_c_stack);
400401
extra_c_stack = NULL;
401402
}
402403
}

src/rt/rust_stack.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ check_stack_canary(stk_seg *stk) {
5353
assert(stk->canary == canary_value && "Somebody killed the canary");
5454
}
5555

56+
// XXX: Duplication here between the local and exchange heap constructors
57+
5658
stk_seg *
5759
create_stack(memory_region *region, size_t sz) {
5860
size_t total_sz = sizeof(stk_seg) + sz;
@@ -69,3 +71,20 @@ destroy_stack(memory_region *region, stk_seg *stk) {
6971
deregister_valgrind_stack(stk);
7072
region->free(stk);
7173
}
74+
75+
stk_seg *
76+
create_exchange_stack(rust_exchange_alloc *exchange, size_t sz) {
77+
size_t total_sz = sizeof(stk_seg) + sz;
78+
stk_seg *stk = (stk_seg *)exchange->malloc(total_sz, false);
79+
memset(stk, 0, sizeof(stk_seg));
80+
stk->end = (uintptr_t) &stk->data[sz];
81+
add_stack_canary(stk);
82+
register_valgrind_stack(stk);
83+
return stk;
84+
}
85+
86+
void
87+
destroy_exchange_stack(rust_exchange_alloc *exchange, stk_seg *stk) {
88+
deregister_valgrind_stack(stk);
89+
exchange->free(stk);
90+
}

0 commit comments

Comments
 (0)