Skip to content

Commit b98df86

Browse files
committed
rt: Move some stack manipulation functions into rust_task
1 parent 4bd8f8d commit b98df86

File tree

2 files changed

+136
-137
lines changed

2 files changed

+136
-137
lines changed

src/rt/rust_task.cpp

Lines changed: 130 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -57,133 +57,8 @@
5757
#endif
5858
#endif
5959

60-
static size_t
61-
get_next_stk_size(rust_task_thread *thread, rust_task *task,
62-
size_t min, size_t current, size_t requested) {
63-
LOG(task, mem, "calculating new stack size for 0x%" PRIxPTR, task);
64-
LOG(task, mem,
65-
"min: %" PRIdPTR " current: %" PRIdPTR " requested: %" PRIdPTR,
66-
min, current, requested);
67-
68-
// Allocate at least enough to accomodate the next frame
69-
size_t sz = std::max(min, requested);
70-
71-
// And double the stack size each allocation
72-
const size_t max = 1024 * 1024;
73-
size_t next = std::min(max, current * 2);
74-
75-
sz = std::max(sz, next);
76-
77-
LOG(task, mem, "next stack size: %" PRIdPTR, sz);
78-
I(thread, requested <= sz);
79-
return sz;
80-
}
81-
82-
// Task stack segments. Heap allocated and chained together.
83-
84-
// The amount of stack in a segment available to Rust code
85-
static size_t
86-
user_stack_size(stk_seg *stk) {
87-
return (size_t)(stk->end
88-
- (uintptr_t)&stk->data[0]
89-
- RED_ZONE_SIZE);
90-
}
91-
92-
static void
93-
free_stk(rust_task *task, stk_seg *stk) {
94-
LOGPTR(task->thread, "freeing stk segment", (uintptr_t)stk);
95-
task->total_stack_sz -= user_stack_size(stk);
96-
task->free(stk);
97-
}
98-
99-
static stk_seg*
100-
new_stk(rust_task_thread *thread, rust_task *task, size_t requested_sz)
101-
{
102-
LOG(task, mem, "creating new stack for task %" PRIxPTR, task);
103-
if (task->stk) {
104-
check_stack_canary(task->stk);
105-
}
106-
107-
// The minimum stack size, in bytes, of a Rust stack, excluding red zone
108-
size_t min_sz = thread->min_stack_size;
109-
110-
// Try to reuse an existing stack segment
111-
if (task->stk != NULL && task->stk->prev != NULL) {
112-
size_t prev_sz = user_stack_size(task->stk->prev);
113-
if (min_sz <= prev_sz && requested_sz <= prev_sz) {
114-
LOG(task, mem, "reusing existing stack");
115-
task->stk = task->stk->prev;
116-
A(thread, task->stk->prev == NULL, "Bogus stack ptr");
117-
config_valgrind_stack(task->stk);
118-
return task->stk;
119-
} else {
120-
LOG(task, mem, "existing stack is not big enough");
121-
free_stk(task, task->stk->prev);
122-
task->stk->prev = NULL;
123-
}
124-
}
125-
126-
// The size of the current stack segment, excluding red zone
127-
size_t current_sz = 0;
128-
if (task->stk != NULL) {
129-
current_sz = user_stack_size(task->stk);
130-
}
131-
// The calculated size of the new stack, excluding red zone
132-
size_t rust_stk_sz = get_next_stk_size(thread, task, min_sz,
133-
current_sz, requested_sz);
134-
135-
if (task->total_stack_sz + rust_stk_sz > thread->env->max_stack_size) {
136-
LOG_ERR(task, task, "task %" PRIxPTR " ran out of stack", task);
137-
task->fail();
138-
}
139-
140-
size_t sz = sizeof(stk_seg) + rust_stk_sz + RED_ZONE_SIZE;
141-
stk_seg *stk = (stk_seg *)task->malloc(sz, "stack");
142-
LOGPTR(task->thread, "new stk", (uintptr_t)stk);
143-
memset(stk, 0, sizeof(stk_seg));
144-
add_stack_canary(stk);
145-
stk->prev = NULL;
146-
stk->next = task->stk;
147-
stk->end = (uintptr_t) &stk->data[rust_stk_sz + RED_ZONE_SIZE];
148-
LOGPTR(task->thread, "stk end", stk->end);
149-
150-
task->stk = stk;
151-
config_valgrind_stack(task->stk);
152-
task->total_stack_sz += user_stack_size(stk);
153-
return stk;
154-
}
155-
156-
static void
157-
del_stk(rust_task *task, stk_seg *stk)
158-
{
159-
assert(stk == task->stk && "Freeing stack segments out of order!");
160-
check_stack_canary(stk);
161-
162-
task->stk = stk->next;
163-
164-
bool delete_stack = false;
165-
if (task->stk != NULL) {
166-
// Don't actually delete this stack. Save it to reuse later,
167-
// preventing the pathological case where we repeatedly reallocate
168-
// the stack for the next frame.
169-
task->stk->prev = stk;
170-
} else {
171-
// This is the last stack, delete it.
172-
delete_stack = true;
173-
}
174-
175-
// Delete the previous previous stack
176-
if (stk->prev != NULL) {
177-
free_stk(task, stk->prev);
178-
stk->prev = NULL;
179-
}
180-
181-
unconfig_valgrind_stack(stk);
182-
if (delete_stack) {
183-
free_stk(task, stk);
184-
A(task->thread, task->total_stack_sz == 0, "Stack size should be 0");
185-
}
186-
}
60+
extern "C" CDECL void
61+
record_sp(void *limit);
18762

18863
// Tasks
18964
rust_task::rust_task(rust_task_thread *thread, rust_task_list *state,
@@ -218,7 +93,7 @@ rust_task::rust_task(rust_task_thread *thread, rust_task_list *state,
21893
LOGPTR(thread, "new task", (uintptr_t)this);
21994
DLOG(thread, task, "sizeof(task) = %d (0x%x)", sizeof *this, sizeof *this);
22095

221-
stk = new_stk(thread, this, init_stack_sz);
96+
new_stack(init_stack_sz);
22297
if (supervisor) {
22398
supervisor->ref();
22499
}
@@ -246,7 +121,7 @@ rust_task::delete_this()
246121
// and no landing pads stopped to clean up.
247122
// FIXME: We should do this when the task exits, not in the destructor
248123
while (stk != NULL) {
249-
del_stk(this, stk);
124+
del_stack();
250125
}
251126

252127
thread->release_task(this);
@@ -630,16 +505,134 @@ rust_task::notify(bool success) {
630505
}
631506
}
632507

633-
extern "C" CDECL void
634-
record_sp(void *limit);
508+
size_t
509+
rust_task::get_next_stack_size(size_t min, size_t current, size_t requested) {
510+
LOG(this, mem, "calculating new stack size for 0x%" PRIxPTR, this);
511+
LOG(this, mem,
512+
"min: %" PRIdPTR " current: %" PRIdPTR " requested: %" PRIdPTR,
513+
min, current, requested);
514+
515+
// Allocate at least enough to accomodate the next frame
516+
size_t sz = std::max(min, requested);
517+
518+
// And double the stack size each allocation
519+
const size_t max = 1024 * 1024;
520+
size_t next = std::min(max, current * 2);
521+
522+
sz = std::max(sz, next);
523+
524+
LOG(this, mem, "next stack size: %" PRIdPTR, sz);
525+
I(thread, requested <= sz);
526+
return sz;
527+
}
528+
529+
// The amount of stack in a segment available to Rust code
530+
static size_t
531+
user_stack_size(stk_seg *stk) {
532+
return (size_t)(stk->end
533+
- (uintptr_t)&stk->data[0]
534+
- RED_ZONE_SIZE);
535+
}
536+
537+
void
538+
rust_task::free_stack(stk_seg *stk) {
539+
LOGPTR(thread, "freeing stk segment", (uintptr_t)stk);
540+
total_stack_sz -= user_stack_size(stk);
541+
free(stk);
542+
}
543+
544+
void
545+
rust_task::new_stack(size_t requested_sz) {
546+
LOG(this, mem, "creating new stack for task %" PRIxPTR, this);
547+
if (stk) {
548+
::check_stack_canary(stk);
549+
}
550+
551+
// The minimum stack size, in bytes, of a Rust stack, excluding red zone
552+
size_t min_sz = thread->min_stack_size;
553+
554+
// Try to reuse an existing stack segment
555+
if (stk != NULL && stk->prev != NULL) {
556+
size_t prev_sz = user_stack_size(stk->prev);
557+
if (min_sz <= prev_sz && requested_sz <= prev_sz) {
558+
LOG(this, mem, "reusing existing stack");
559+
stk = stk->prev;
560+
A(thread, stk->prev == NULL, "Bogus stack ptr");
561+
config_valgrind_stack(stk);
562+
return;
563+
} else {
564+
LOG(this, mem, "existing stack is not big enough");
565+
free_stack(stk->prev);
566+
stk->prev = NULL;
567+
}
568+
}
569+
570+
// The size of the current stack segment, excluding red zone
571+
size_t current_sz = 0;
572+
if (stk != NULL) {
573+
current_sz = user_stack_size(stk);
574+
}
575+
// The calculated size of the new stack, excluding red zone
576+
size_t rust_stk_sz = get_next_stack_size(min_sz,
577+
current_sz, requested_sz);
578+
579+
if (total_stack_sz + rust_stk_sz > thread->env->max_stack_size) {
580+
LOG_ERR(this, task, "task %" PRIxPTR " ran out of stack", this);
581+
fail();
582+
}
583+
584+
size_t sz = sizeof(stk_seg) + rust_stk_sz + RED_ZONE_SIZE;
585+
stk_seg *new_stk = (stk_seg *)malloc(sz, "stack");
586+
LOGPTR(thread, "new stk", (uintptr_t)new_stk);
587+
memset(new_stk, 0, sizeof(stk_seg));
588+
add_stack_canary(new_stk);
589+
new_stk->prev = NULL;
590+
new_stk->next = stk;
591+
new_stk->end = (uintptr_t) &new_stk->data[rust_stk_sz + RED_ZONE_SIZE];
592+
LOGPTR(thread, "stk end", new_stk->end);
593+
594+
stk = new_stk;
595+
config_valgrind_stack(stk);
596+
total_stack_sz += user_stack_size(new_stk);
597+
}
598+
599+
void
600+
rust_task::del_stack() {
601+
stk_seg *old_stk = stk;
602+
::check_stack_canary(old_stk);
603+
604+
stk = old_stk->next;
605+
606+
bool delete_stack = false;
607+
if (stk != NULL) {
608+
// Don't actually delete this stack. Save it to reuse later,
609+
// preventing the pathological case where we repeatedly reallocate
610+
// the stack for the next frame.
611+
stk->prev = old_stk;
612+
} else {
613+
// This is the last stack, delete it.
614+
delete_stack = true;
615+
}
616+
617+
// Delete the previous previous stack
618+
if (old_stk->prev != NULL) {
619+
free_stack(old_stk->prev);
620+
old_stk->prev = NULL;
621+
}
622+
623+
unconfig_valgrind_stack(old_stk);
624+
if (delete_stack) {
625+
free_stack(old_stk);
626+
A(thread, total_stack_sz == 0, "Stack size should be 0");
627+
}
628+
}
635629

636630
void *
637631
rust_task::next_stack(size_t stk_sz, void *args_addr, size_t args_sz) {
638-
639-
stk_seg *stk_seg = new_stk(thread, this, stk_sz + args_sz);
640-
A(thread, stk_seg->end - (uintptr_t)stk_seg->data >= stk_sz + args_sz,
632+
new_stack(stk_sz + args_sz);
633+
A(thread, stk->end - (uintptr_t)stk->data >= stk_sz + args_sz,
641634
"Did not receive enough stack");
642-
uint8_t *new_sp = (uint8_t*)stk_seg->end;
635+
uint8_t *new_sp = (uint8_t*)stk->end;
643636
// Push the function arguments to the new stack
644637
new_sp = align_down(new_sp - args_sz);
645638
memcpy(new_sp, args_addr, args_sz);
@@ -651,7 +644,7 @@ rust_task::next_stack(size_t stk_sz, void *args_addr, size_t args_sz) {
651644

652645
void
653646
rust_task::prev_stack() {
654-
del_stk(this, stk);
647+
del_stack();
655648
A(thread, rust_task_thread::get_task() == this,
656649
"Recording the stack limit for the wrong thread");
657650
record_stack_limit();
@@ -695,7 +688,7 @@ void
695688
rust_task::reset_stack_limit() {
696689
uintptr_t sp = get_sp();
697690
while (!sp_in_stk_seg(sp, stk)) {
698-
del_stk(this, stk);
691+
del_stack();
699692
A(thread, stk != NULL, "Failed to find the current stack");
700693
}
701694
record_stack_limit();

src/rt/rust_task.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ rust_task : public kernel_owned<rust_task>, rust_cond
106106
private:
107107
// Called when the atomic refcount reaches zero
108108
void delete_this();
109+
110+
void new_stack(size_t sz);
111+
void del_stack();
112+
void free_stack(stk_seg *stk);
113+
size_t get_next_stack_size(size_t min, size_t current, size_t requested);
114+
109115
public:
110116

111117
// Only a pointer to 'name' is kept, so it must live as long as this task.

0 commit comments

Comments
 (0)