Skip to content

Commit 2c1ec67

Browse files
committed
Lots of changes around memory managment in the Runtime. Added memory regions and fixed race caused by calling rust_srv::malloc() from multiple threads when sending messages.
1 parent 9fa2b53 commit 2c1ec67

13 files changed

+303
-126
lines changed

src/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ RUNTIME_CS := rt/sync/timer.cpp \
266266
rt/rust_timer.cpp \
267267
rt/circular_buffer.cpp \
268268
rt/isaac/randport.cpp \
269-
rt/rust_srv.cpp
269+
rt/rust_srv.cpp \
270+
rt/memory_region.cpp
270271

271272
RUNTIME_HDR := rt/globals.h \
272273
rt/rust.h \
@@ -285,7 +286,9 @@ RUNTIME_HDR := rt/globals.h \
285286
rt/util/hash_map.h \
286287
rt/sync/sync.h \
287288
rt/sync/timer.h \
288-
rt/rust_srv.h
289+
rt/rust_srv.h \
290+
rt/memory_region.h \
291+
rt/memory.h
289292

290293
RUNTIME_INCS := -Irt/isaac -Irt/uthash
291294
RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX))

src/rt/memory.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
*
3+
*/
4+
5+
#ifndef MEMORY_H
6+
#define MEMORY_H
7+
8+
9+
inline void *operator new(size_t size, void *mem) {
10+
return mem;
11+
}
12+
13+
inline void *operator new(size_t size, rust_dom *dom) {
14+
return dom->malloc(size, memory_region::LOCAL);
15+
}
16+
17+
inline void *operator new[](size_t size, rust_dom *dom) {
18+
return dom->malloc(size, memory_region::LOCAL);
19+
}
20+
21+
inline void *operator new(size_t size, rust_dom &dom) {
22+
return dom.malloc(size, memory_region::LOCAL);
23+
}
24+
25+
inline void *operator new[](size_t size, rust_dom &dom) {
26+
return dom.malloc(size, memory_region::LOCAL);
27+
}
28+
29+
inline void *operator new(size_t size, rust_dom *dom,
30+
memory_region::memory_region_type type) {
31+
return dom->malloc(size, type);
32+
}
33+
34+
inline void *operator new[](size_t size, rust_dom *dom,
35+
memory_region::memory_region_type type) {
36+
return dom->malloc(size, type);
37+
}
38+
39+
inline void *operator new(size_t size, rust_dom &dom,
40+
memory_region::memory_region_type type) {
41+
return dom.malloc(size, type);
42+
}
43+
44+
inline void *operator new[](size_t size, rust_dom &dom,
45+
memory_region::memory_region_type type) {
46+
return dom.malloc(size, type);
47+
}
48+
49+
inline void operator delete(void *mem, rust_dom *dom) {
50+
dom->free(mem, memory_region::LOCAL);
51+
return;
52+
}
53+
54+
inline void operator delete(void *mem, rust_dom *dom,
55+
memory_region::memory_region_type type) {
56+
dom->free(mem, type);
57+
return;
58+
}
59+
60+
#endif /* MEMORY_H */

src/rt/memory_region.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
*
3+
*/
4+
5+
#include "rust_internal.h"
6+
#include "memory_region.h"
7+
8+
#define TRACK_ALLOCATIONS
9+
10+
memory_region::memory_region(rust_srv *srv, bool synchronized) :
11+
_srv(srv), _parent(NULL), _live_allocations(0),
12+
_synchronized(synchronized) {
13+
// Nop.
14+
}
15+
16+
memory_region::memory_region(memory_region *parent) :
17+
_srv(parent->_srv), _parent(parent), _live_allocations(0),
18+
_synchronized(parent->_synchronized) {
19+
// Nop.
20+
}
21+
22+
void memory_region::free(void *mem) {
23+
if (_synchronized) { _lock.lock(); }
24+
#ifdef TRACK_ALLOCATIONS
25+
if (_allocation_list.replace(mem, NULL) == false) {
26+
printf("free: ptr 0x%" PRIxPTR " is not in allocation_list\n",
27+
(uintptr_t) mem);
28+
_srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
29+
}
30+
#endif
31+
if (_live_allocations < 1) {
32+
_srv->fatal("live_allocs < 1", __FILE__, __LINE__, "");
33+
}
34+
_live_allocations--;
35+
_srv->free(mem);
36+
if (_synchronized) { _lock.unlock(); }
37+
38+
}
39+
40+
void *
41+
memory_region::realloc(void *mem, size_t size) {
42+
if (_synchronized) { _lock.lock(); }
43+
if (!mem) {
44+
_live_allocations++;
45+
}
46+
void *newMem = _srv->realloc(mem, size);
47+
#ifdef TRACK_ALLOCATIONS
48+
if (_allocation_list.replace(mem, newMem) == false) {
49+
printf("realloc: ptr 0x%" PRIxPTR " is not in allocation_list\n",
50+
(uintptr_t) mem);
51+
_srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
52+
}
53+
#endif
54+
if (_synchronized) { _lock.unlock(); }
55+
return newMem;
56+
}
57+
58+
void *
59+
memory_region::malloc(size_t size) {
60+
if (_synchronized) { _lock.lock(); }
61+
_live_allocations++;
62+
void *mem = _srv->malloc(size);
63+
#ifdef TRACK_ALLOCATIONS
64+
_allocation_list.append(mem);
65+
#endif
66+
if (_synchronized) { _lock.unlock(); }
67+
return mem;
68+
}
69+
70+
void *
71+
memory_region::calloc(size_t size) {
72+
if (_synchronized) { _lock.lock(); }
73+
_live_allocations++;
74+
void *mem = _srv->malloc(size);
75+
memset(mem, 0, size);
76+
#ifdef TRACK_ALLOCATIONS
77+
_allocation_list.append(mem);
78+
#endif
79+
if (_synchronized) { _lock.unlock(); }
80+
return mem;
81+
}
82+
83+
memory_region::~memory_region() {
84+
if (_live_allocations == 0) {
85+
return;
86+
}
87+
char msg[128];
88+
snprintf(msg, sizeof(msg),
89+
"leaked memory in rust main loop (%" PRIuPTR " objects)",
90+
_live_allocations);
91+
#ifdef TRACK_ALLOCATIONS
92+
for (size_t i = 0; i < _allocation_list.size(); i++) {
93+
if (_allocation_list[i] != NULL) {
94+
printf("allocation 0x%" PRIxPTR " was not freed\n",
95+
(uintptr_t) _allocation_list[i]);
96+
}
97+
}
98+
#endif
99+
_srv->fatal(msg, __FILE__, __LINE__, "%d objects", _live_allocations);
100+
}

src/rt/memory_region.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* The Rust runtime uses memory regions to provide a primitive level of
3+
* memory management and isolation between tasks, and domains.
4+
*
5+
* TODO: Implement a custom lock-free malloc / free instead of relying solely
6+
* on the standard malloc / free.
7+
*/
8+
9+
#ifndef MEMORY_REGION_H
10+
#define MEMORY_REGION_H
11+
12+
#include "sync/spin_lock.h"
13+
14+
class rust_srv;
15+
16+
class memory_region {
17+
private:
18+
rust_srv *_srv;
19+
memory_region *_parent;
20+
size_t _live_allocations;
21+
array_list<void *> _allocation_list;
22+
const bool _synchronized;
23+
spin_lock _lock;
24+
public:
25+
enum memory_region_type {
26+
LOCAL = 0x1, SYNCHRONIZED = 0x2
27+
};
28+
memory_region(rust_srv *srv, bool synchronized);
29+
memory_region(memory_region *parent);
30+
void *malloc(size_t size);
31+
void *calloc(size_t size);
32+
void *realloc(void *mem, size_t size);
33+
void free(void *mem);
34+
virtual ~memory_region();
35+
};
36+
37+
#endif /* MEMORY_REGION_H */

src/rt/rust.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121

2222
#include "rust_srv.h"
2323

24-
inline void *operator new(size_t size, rust_srv *srv)
25-
{
26-
return srv->malloc(size);
27-
}
28-
2924
/*
3025
* Local Variables:
3126
* fill-column: 78;

src/rt/rust_builtin.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ last_os_error(rust_task *task) {
3838
#endif
3939
size_t fill = strlen(buf) + 1;
4040
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
41-
void *mem = dom->malloc(alloc);
41+
void *mem = dom->malloc(alloc, memory_region::LOCAL);
4242
if (!mem) {
4343
task->fail(1);
4444
return NULL;
@@ -134,7 +134,7 @@ str_alloc_with_data(rust_task *task,
134134
{
135135
rust_dom *dom = task->dom;
136136
size_t alloc = next_power_of_two(sizeof(rust_str) + n_bytes);
137-
void *mem = dom->malloc(alloc);
137+
void *mem = dom->malloc(alloc, memory_region::LOCAL);
138138
if (!mem)
139139
return NULL;
140140
rust_str *st = new (mem) rust_str(dom, alloc, fill, d);

src/rt/rust_dom.cpp

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ rust_dom::rust_dom(rust_srv *srv, rust_crate const *root_crate,
1313
root_crate(root_crate),
1414
_log(srv, this),
1515
srv(srv),
16+
local_region(&srv->local_region),
17+
synchronized_region(&srv->synchronized_region),
1618
name(name),
1719
running_tasks(this),
1820
blocked_tasks(this),
@@ -144,36 +146,65 @@ rust_dom::fail() {
144146
}
145147

146148
void *
147-
rust_dom::malloc(size_t sz) {
148-
void *p = srv->malloc(sz);
149-
I(this, p);
150-
log(rust_log::MEM,
151-
"%s @0x%" PRIxPTR " rust_dom::malloc(%d) -> 0x%" PRIxPTR,
152-
name, (uintptr_t) this, sz, p);
153-
return p;
149+
rust_dom::malloc(size_t size) {
150+
return malloc(size, memory_region::LOCAL);
154151
}
155152

156153
void *
157-
rust_dom::calloc(size_t sz) {
158-
void *p = this->malloc(sz);
159-
memset(p, 0, sz);
160-
return p;
154+
rust_dom::malloc(size_t size, memory_region::memory_region_type type) {
155+
if (type == memory_region::LOCAL) {
156+
return local_region.malloc(size);
157+
} else if (type == memory_region::SYNCHRONIZED) {
158+
return synchronized_region.malloc(size);
159+
}
160+
return NULL;
161+
}
162+
163+
void *
164+
rust_dom::calloc(size_t size) {
165+
return calloc(size, memory_region::LOCAL);
166+
}
167+
168+
void *
169+
rust_dom::calloc(size_t size, memory_region::memory_region_type type) {
170+
if (type == memory_region::LOCAL) {
171+
return local_region.calloc(size);
172+
} else if (type == memory_region::SYNCHRONIZED) {
173+
return synchronized_region.calloc(size);
174+
}
175+
return NULL;
161176
}
162177

163178
void *
164-
rust_dom::realloc(void *p, size_t sz) {
165-
void *p1 = srv->realloc(p, sz);
166-
I(this, p1);
167-
log(rust_log::MEM, "rust_dom::realloc(0x%" PRIxPTR ", %d) -> 0x%" PRIxPTR,
168-
p, sz, p1);
169-
return p1;
179+
rust_dom::realloc(void *mem, size_t size) {
180+
return realloc(mem, size, memory_region::LOCAL);
181+
}
182+
183+
void *
184+
rust_dom::realloc(void *mem, size_t size,
185+
memory_region::memory_region_type type) {
186+
if (type == memory_region::LOCAL) {
187+
return local_region.realloc(mem, size);
188+
} else if (type == memory_region::SYNCHRONIZED) {
189+
return synchronized_region.realloc(mem, size);
190+
}
191+
return NULL;
170192
}
171193

172194
void
173-
rust_dom::free(void *p) {
174-
log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", p);
175-
I(this, p);
176-
srv->free(p);
195+
rust_dom::free(void *mem) {
196+
free(mem, memory_region::LOCAL);
197+
}
198+
199+
void
200+
rust_dom::free(void *mem, memory_region::memory_region_type type) {
201+
log(rust_log::MEM, "rust_dom::free(0x%" PRIxPTR ")", mem);
202+
if (type == memory_region::LOCAL) {
203+
local_region.free(mem);
204+
} else if (type == memory_region::SYNCHRONIZED) {
205+
synchronized_region.free(mem);
206+
}
207+
return;
177208
}
178209

179210
#ifdef __WIN32__
@@ -264,7 +295,6 @@ void rust_dom::send_message(rust_message *message) {
264295
message,
265296
&_incoming_message_queue,
266297
this);
267-
A(this, message->dom == this, "Message owned by non-local domain.");
268298
_incoming_message_queue.enqueue(message);
269299
}
270300

@@ -277,7 +307,8 @@ void rust_dom::drain_incoming_message_queue() {
277307
log(rust_log::COMM, "<== processing incoming message \"%s\" 0x%"
278308
PRIxPTR, message->label, message);
279309
message->process();
280-
delete message;
310+
message->~rust_message();
311+
this->synchronized_region.free(message);
281312
}
282313
}
283314

@@ -322,8 +353,7 @@ rust_dom::get_port_proxy_synchronized(rust_port *port) {
322353
* Returns NULL if no tasks can be scheduled.
323354
*/
324355
rust_task *
325-
rust_dom::schedule_task()
326-
{
356+
rust_dom::schedule_task() {
327357
I(this, this);
328358
// FIXME: in the face of failing tasks, this is not always right.
329359
// I(this, n_live_tasks() > 0);

0 commit comments

Comments
 (0)