mirror of
https://github.com/clearlinux/graphene.git
synced 2026-10-03 23:48:19 +00:00
When child thread exits, it wakes up its parent if CLONE_CHILD_CLEARTID was set during clone() call. Previously, this was done by the child thread itself as part of its own clean-up in release_clear_child_id(). But this child thread is still alive at this point and uses some resources, most notably the stack (that might have been provided by the parent) and the SGX TCS slot. Upon waking up, the parent might decide to free that stack (as Pthreads do) or re-use the TCS slot, causing data races. This commit introduces a correct emulation of CLONE_CHILD_CLEARTID: - A new argument `PAL_PTR clear_child_tid` is added to DkThreadExit(); it points to internal Graphene memory that is erased on child exit to notify Async Helper thread. - At PAL layer, when thread finally exits, it sets PAL-level *clear_child_tid = 0 (corresponds to &clear_child_tid_val_pal at LibOS level); this signals to LibOS layer that the thread stopped using resources. - At LibOS layer, Async Helper thread is set up to wait for the signal from PAL; it is now the responsibility of Async Helper thread to call release_clear_child_id() to wake up the parent thread. - Async Helper thread waits for clear_child_tid_val_pal == 0 and then sets the actual clear_child_tid to 0 and wakes up the waiting parent. Note that for Linux-SGX PAL, clear_child_tid is set to 0 not immediately but as part of handle_thread_reset, otherwise the TCS slot could be still occupied when LibOS wakes up the parent. As a side effect, the LibOS code for threads/process exit is cleaned up. This commit also fixes all regression tests to use the new signature of DkThreadExit() and increases the number of SGX threads slightly (to accommodate the newly used Async Helper thread).
94 lines
2.2 KiB
C
94 lines
2.2 KiB
C
#include <stdatomic.h>
|
|
|
|
#include "pal.h"
|
|
#include "pal_debug.h"
|
|
|
|
volatile bool dummy_true = true;
|
|
|
|
static atomic_bool thread2_started = false;
|
|
static atomic_bool thread3_started = false;
|
|
static atomic_bool thread3_exit_ok = true;
|
|
static atomic_bool thread4_started = false;
|
|
|
|
int thread2_run(void* args) {
|
|
pal_printf("Thread 2 started.\n");
|
|
|
|
thread2_started = true;
|
|
|
|
pal_printf("Exiting thread 2 by return.\n");
|
|
return 0;
|
|
}
|
|
|
|
int thread3_run(void* args) {
|
|
pal_printf("Thread 3 started.\n");
|
|
|
|
thread3_started = true;
|
|
|
|
pal_printf("Exiting thread 3 by DkThreadExit.\n");
|
|
|
|
// Ensure that the compiler can't know that this should never return.
|
|
if (dummy_true) {
|
|
DkThreadExit(/*clear_child_tid=*/NULL);
|
|
}
|
|
|
|
thread3_exit_ok = false;
|
|
pal_printf("Exiting thread 3 failed.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int thread4_run(void* args) {
|
|
pal_printf("Thread 4 started.\n");
|
|
|
|
thread4_started = true;
|
|
|
|
pal_printf("Exiting thread 4 by return.\n");
|
|
return 0;
|
|
}
|
|
|
|
// If there's a thread limit, like on SGX, it should be set to exactly 2. There
|
|
// should be only the main thread and only one other thread at a time.
|
|
int main() {
|
|
pal_printf("Thread 1 (main) started.\n");
|
|
|
|
PAL_HANDLE thread2 = DkThreadCreate(thread2_run, NULL);
|
|
if (!thread2) {
|
|
pal_printf("DkThreadCreate failed for thread 2.\n");
|
|
return 1;
|
|
}
|
|
|
|
// 1 s should be enough even on a very busy system to start a thread and
|
|
// then exit it again including all cleanup.
|
|
DkThreadDelayExecution(1000000);
|
|
|
|
if (thread2_started) {
|
|
pal_printf("Thread 2 ok.\n");
|
|
}
|
|
|
|
PAL_HANDLE thread3 = DkThreadCreate(thread3_run, NULL);
|
|
if (!thread3) {
|
|
pal_printf("DkThreadCreate failed for thread 3.\n");
|
|
return 1;
|
|
}
|
|
|
|
DkThreadDelayExecution(1000000);
|
|
|
|
if (thread3_started && thread3_exit_ok) {
|
|
pal_printf("Thread 3 ok.\n");
|
|
}
|
|
|
|
PAL_HANDLE thread4 = DkThreadCreate(thread4_run, NULL);
|
|
if (!thread4) {
|
|
pal_printf("DkThreadCreate failed for thread 4.\n");
|
|
return 1;
|
|
}
|
|
|
|
DkThreadDelayExecution(1000000);
|
|
|
|
if (thread4_started) {
|
|
pal_printf("Thread 4 ok.\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|