occlum/test/exit_group/main.c
Tate, Hongliang Tian b585fce65d Add the interrupt subsystem
Before this commit, events like signals and exit_group are handled by
LibOS threads in a cooperative fashion: if the user code executed by a
LibOS thread does not invoke system calls (e.g., a busy loop), then the LibOS
won't have any opportunity to take control and handle events.

With the help from the POSIX signal-based interrupt mechanism of
Occlum's version of Intel SGX SDK, the LibOS can now interrupt the
execution of arbitrary user code in a LibOS thread by sending real-time
POSIX signals (the signal number is 64) to it. These signals are sent by
a helper thread spawn by Occlum PAL. The helper thread periodically
enters into the enclave to check if there are any LibOS threads with
pending events. If any, the helper thread broadcast POSIX signals to
them. When interrupted by a signal, the receiver LibOS thread may be in
one of the two previously problematic states in terms of event handling:

1. Executing non-cooperative user code (e.g., a busy loop). In this
case, the signal will trigger an interrupt handler inside the enclave,
which can then enter the LibOS kernel to deal with any pending events.

2. Executing an OCall that invokes blocking system calls (e.g., futex,
nanosleep, or blocking I/O). In this case, the signal will interrupt the
blocking system call so that the OCall can return back to the enclave.

Thanks to the new interrupt subsystem, some event-based system calls
are made robust. One such example is exit_group. We can now guarantee
that exit_group can force any thread in a process to exit.
2020-07-10 11:52:01 +00:00

79 lines
2.4 KiB
C

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <linux/futex.h>
#include "test.h"
// ============================================================================
// Test case
// ============================================================================
//
// Three types of threads that will not exit voluntarily
//
// Type 1: a busy loop thread
static void *busyloop_thread_func(void *_) {
while (1) { }
return NULL;
}
// Type 2: a sleeping thread
static void *sleeping_thread_func(void *_) {
unsigned int a_year_in_sec = 365 * 24 * 60 * 60;
sleep(a_year_in_sec);
return NULL;
}
// Type 3: a thead that waits on a futex FOREVER
static void *futex_wait_thread_func(void *_) {
int my_private_futex = 0;
syscall(SYS_futex, &my_private_futex, FUTEX_WAIT, my_private_futex, NULL);
return NULL;
}
// exit_group syscall should terminate all threads in a thread group.
int test_exit_group_to_force_threads_terminate(void) {
// Create three types of threads that will not exit voluntarily
pthread_t busyloop_thread;
if (pthread_create(&busyloop_thread, NULL, busyloop_thread_func, NULL) < 0) {
printf("ERROR: pthread_create failed\n");
return -1;
}
pthread_t sleeping_thread;
if (pthread_create(&sleeping_thread, NULL, sleeping_thread_func, NULL) < 0) {
printf("ERROR: pthread_create failed\n");
return -1;
}
pthread_t futex_wait_thread;
if (pthread_create(&futex_wait_thread, NULL, futex_wait_thread_func, NULL) < 0) {
printf("ERROR: pthread_create failed\n");
return -1;
}
// Sleep for a while to make sure all three threads are running
useconds_t half_second = 500 * 1000; // in us
usleep(half_second);
// exit_group syscall will be called eventually by libc's exit, after the
// main function returns. If Occlum can terminate normally, this means
// exit_group syscall taking effect.
return 0;
}
// ============================================================================
// Test suite
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_exit_group_to_force_threads_terminate)
};
int main() {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}