Skip to content

Commit 36d5181

Browse files
committed
rt: Change the lifecycle of tasks and schedulers for various reasons
This is in preparation for giving schedulers their own life cycle separate from the kernel. Tasks must be deleted before their scheduler thread, so we can't let the scheduler exit before all its tasks have been cleaned up. In this scheme, the scheduler will unregister tasks with the kernel when they are reaped, then drop their ref on the task (there may still be others). When the task ref count hits zero, the task will request to be unregistered from the scheduler, which is responsible for deleting the task. Instead of having the kernel tell the scheduler to exit, let the scheduler decide when to exit. For now it will exit when all of its tasks are unregistered.
1 parent b0a24d3 commit 36d5181

File tree

8 files changed

+66
-23
lines changed

8 files changed

+66
-23
lines changed

src/rt/rust_internal.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,14 @@ static size_t const BUF_BYTES = 2048;
104104
void ref() { ++ref_count; } \
105105
void deref() { if (--ref_count == 0) { dtor; } }
106106

107-
#define RUST_ATOMIC_REFCOUNT() \
108-
private: \
109-
intptr_t ref_count; \
110-
public: \
111-
void ref() { \
112-
intptr_t old = sync::increment(ref_count); \
113-
assert(old > 0); \
114-
} \
115-
void deref() { if(0 == sync::decrement(ref_count)) { delete this; } }
107+
#define RUST_ATOMIC_REFCOUNT() \
108+
public: \
109+
intptr_t ref_count; \
110+
void ref() { \
111+
intptr_t old = sync::increment(ref_count); \
112+
assert(old > 0); \
113+
} \
114+
void deref() { if(0 == sync::decrement(ref_count)) { delete_this(); } }
116115

117116
template <typename T> struct task_owned {
118117
inline void *operator new(size_t size, rust_task *task, const char *tag);

src/rt/rust_kernel.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,6 @@ rust_kernel::release_task_id(rust_task_id id) {
129129
new_live_tasks = --live_tasks;
130130
}
131131
KLOG_("Total outstanding tasks: %d", new_live_tasks);
132-
if (new_live_tasks == 0) {
133-
// There are no more tasks and there never will be.
134-
// Tell all the schedulers to exit.
135-
sched->exit();
136-
}
137132
}
138133

139134
rust_task *

src/rt/rust_scheduler.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rust_scheduler::rust_scheduler(rust_kernel *kernel,
99
srv(srv),
1010
env(srv->env),
1111
live_threads(num_threads),
12+
live_tasks(0),
1213
num_threads(num_threads),
1314
id(id)
1415
{
@@ -84,6 +85,7 @@ rust_scheduler::create_task(rust_task *spawner, const char *name,
8485
{
8586
scoped_lock with(lock);
8687
thread_no = isaac_rand(&rctx) % num_threads;
88+
live_tasks++;
8789
}
8890
rust_task_thread *thread = threads[thread_no];
8991
return thread->create_task(spawner, name, init_stack_sz);
@@ -94,9 +96,28 @@ rust_scheduler::create_task(rust_task *spawner, const char *name) {
9496
return create_task(spawner, name, env->min_stack_size);
9597
}
9698

99+
void
100+
rust_scheduler::release_task() {
101+
bool need_exit = false;
102+
{
103+
scoped_lock with(lock);
104+
live_tasks--;
105+
if (live_tasks == 0) {
106+
need_exit = true;
107+
}
108+
}
109+
if (need_exit) {
110+
// There are no more tasks on this scheduler. Time to leave
111+
exit();
112+
}
113+
}
114+
97115
void
98116
rust_scheduler::exit() {
99-
for(size_t i = 0; i < num_threads; ++i) {
117+
// Take a copy of num_threads. After the last thread exits this
118+
// scheduler will get destroyed, and our fields will cease to exist.
119+
size_t current_num_threads = num_threads;
120+
for(size_t i = 0; i < current_num_threads; ++i) {
100121
threads[i]->exit();
101122
}
102123
}

src/rt/rust_scheduler.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class rust_scheduler : public kernel_owned<rust_scheduler> {
1414
lock_and_signal lock;
1515
// When this hits zero we'll tell the kernel to release us
1616
uintptr_t live_threads;
17+
// When this hits zero we'll tell the threads to exit
18+
uintptr_t live_tasks;
1719
randctx rctx;
1820

1921
array_list<rust_task_thread *> threads;
@@ -27,6 +29,8 @@ class rust_scheduler : public kernel_owned<rust_scheduler> {
2729
rust_task_thread *create_task_thread(int id);
2830
void destroy_task_thread(rust_task_thread *thread);
2931

32+
void exit();
33+
3034
public:
3135
rust_scheduler(rust_kernel *kernel, rust_srv *srv, size_t num_threads,
3236
rust_sched_id id);
@@ -39,7 +43,8 @@ class rust_scheduler : public kernel_owned<rust_scheduler> {
3943
size_t init_stack_sz);
4044
rust_task_id create_task(rust_task *spawner, const char *name);
4145

42-
void exit();
46+
void release_task();
47+
4348
size_t number_of_threads();
4449
// Called by each thread when it terminates. When all threads
4550
// terminate the scheduler does as well.

src/rt/rust_task.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ rust_task::rust_task(rust_task_thread *thread, rust_task_list *state,
268268
}
269269
}
270270

271-
rust_task::~rust_task()
271+
void
272+
rust_task::delete_this()
272273
{
273274
I(thread, !thread->lock.lock_held_by_current_thread());
274275
I(thread, port_table.is_empty());
@@ -291,6 +292,8 @@ rust_task::~rust_task()
291292
while (stk != NULL) {
292293
del_stk(this, stk);
293294
}
295+
296+
thread->release_task(this);
294297
}
295298

296299
struct spawn_args {

src/rt/rust_task.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,18 @@ rust_task : public kernel_owned<rust_task>, rust_cond
122122
// The amount of stack we're using, excluding red zones
123123
size_t total_stack_sz;
124124

125+
private:
126+
// Called when the atomic refcount reaches zero
127+
void delete_this();
128+
public:
129+
125130
// Only a pointer to 'name' is kept, so it must live as long as this task.
126131
rust_task(rust_task_thread *thread,
127132
rust_task_list *state,
128133
rust_task *spawner,
129134
const char *name,
130135
size_t init_stack_sz);
131136

132-
~rust_task();
133-
134137
void start(spawn_fn spawnee_fn,
135138
rust_opaque_box *env,
136139
void *args);

src/rt/rust_task_thread.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,30 @@ rust_task_thread::reap_dead_tasks() {
136136

137137
for (size_t i = 0; i < dead_tasks_len; ++i) {
138138
rust_task *task = dead_tasks_copy[i];
139-
if (task) {
140-
kernel->release_task_id(task->user.id);
141-
task->deref();
142-
}
139+
// Release the task from the kernel so nobody else can get at it
140+
kernel->release_task_id(task->user.id);
141+
// Deref the task, which may cause it to request us to release it
142+
task->deref();
143143
}
144144
srv->free(dead_tasks_copy);
145145

146146
lock.lock();
147147
}
148148

149+
void
150+
rust_task_thread::release_task(rust_task *task) {
151+
// Nobody should have a ref to the task at this point
152+
I(this, task->ref_count == 0);
153+
// Kernel should not know about the task any more
154+
I(this, kernel->get_task_by_id(task->user.id) == NULL);
155+
// Now delete the task, which will require using this thread's
156+
// memory region.
157+
delete task;
158+
// Now release the task from the scheduler, which may trigger this
159+
// thread to exit
160+
sched->release_task();
161+
}
162+
149163
/**
150164
* Schedules a running task for execution. Only running tasks can be
151165
* activated. Blocked tasks have to be unblocked before they can be

src/rt/rust_task_thread.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ struct rust_task_thread : public kernel_owned<rust_task_thread>,
122122

123123
static rust_task *get_task();
124124

125+
// Called by each task when they are ready to be destroyed
126+
void release_task(rust_task *task);
127+
125128
// Tells the scheduler to exit it's scheduling loop and thread
126129
void exit();
127130
};

0 commit comments

Comments
 (0)