2020-01-18 11:38:21 +03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2020-08-05 21:03:21 +03:00
# include <LibCore/ArgsParser.h>
2022-03-29 22:36:41 +03:00
# include <LibMain/Main.h>
2021-05-14 17:32:57 +03:00
# include <errno.h>
2019-11-14 22:58:23 +03:00
# include <pthread.h>
2023-01-07 20:54:01 +03:00
# include <signal.h>
2019-11-14 22:58:23 +03:00
# include <stdio.h>
2019-11-18 06:08:10 +03:00
# include <string.h>
2021-05-14 18:38:33 +03:00
# include <sys/mman.h>
2019-11-18 06:08:10 +03:00
# include <unistd.h>
2019-11-14 22:58:23 +03:00
2019-11-16 14:19:58 +03:00
static int mutex_test ( ) ;
2019-11-18 06:08:10 +03:00
static int detached_test ( ) ;
static int priority_test ( ) ;
static int stack_size_test ( ) ;
2020-08-09 18:33:13 +03:00
static int staying_alive_test ( ) ;
2019-11-18 06:08:10 +03:00
static int set_stack_test ( ) ;
2021-07-09 04:56:29 +03:00
static int kill_test ( ) ;
2019-11-16 14:19:58 +03:00
2022-03-29 22:36:41 +03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-11-14 22:58:23 +03:00
{
2023-02-28 23:41:43 +03:00
StringView test_name = " n " sv ;
2020-08-05 21:03:21 +03:00
Core : : ArgsParser args_parser ;
2020-12-05 18:22:58 +03:00
args_parser . set_general_help (
" Exercise error-handling and edge-case paths of the execution environment "
" (i.e., Kernel or UE) by doing unusual thread-related things. " ) ;
2021-07-09 04:56:29 +03:00
args_parser . add_positional_argument ( test_name , " Test to run (m = mutex, d = detached, p = priority, s = stack size, t = simple thread test, x = set stack, k = kill, nothing = join race) " , " test-name " , Core : : ArgsParser : : Required : : No ) ;
2022-03-29 22:36:41 +03:00
args_parser . parse ( arguments ) ;
2020-08-05 21:03:21 +03:00
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' m ' )
2019-11-16 14:19:58 +03:00
return mutex_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' d ' )
2019-11-18 06:08:10 +03:00
return detached_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' p ' )
2019-11-18 06:08:10 +03:00
return priority_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' s ' )
2019-11-18 06:08:10 +03:00
return stack_size_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' t ' )
2020-08-09 18:33:13 +03:00
return staying_alive_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' x ' )
2019-11-18 06:08:10 +03:00
return set_stack_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] = = ' k ' )
2021-07-09 04:56:29 +03:00
return kill_test ( ) ;
2023-02-28 23:41:43 +03:00
if ( test_name [ 0 ] ! = ' n ' ) {
2023-02-21 14:44:41 +03:00
args_parser . print_usage ( stdout , arguments . strings [ 0 ] ) ;
2020-09-03 07:35:19 +03:00
return 1 ;
}
2019-11-16 14:19:58 +03:00
2021-05-31 17:43:25 +03:00
outln ( " Hello from the first thread! " ) ;
2019-11-14 22:58:23 +03:00
pthread_t thread_id ;
2019-11-16 14:19:58 +03:00
int rc = pthread_create (
& thread_id , nullptr , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " Hi there, from the second thread! " ) ;
2019-11-16 14:19:58 +03:00
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
2019-11-14 22:58:23 +03:00
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 1 ;
}
void * retval ;
rc = pthread_join ( thread_id , & retval ) ;
if ( rc < 0 ) {
perror ( " pthread_join " ) ;
return 1 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Okay, joined and got retval={} " , retval ) ;
2019-11-14 22:58:23 +03:00
return 0 ;
}
2019-11-16 14:19:58 +03:00
static pthread_mutex_t mutex ;
int mutex_test ( )
{
int rc = pthread_mutex_init ( & mutex , nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_mutex_init " ) ;
return 1 ;
}
pthread_t thread_id ;
rc = pthread_create (
& thread_id , nullptr , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2019-11-16 14:19:58 +03:00
for ( ; ; ) {
pthread_mutex_lock ( & mutex ) ;
2021-05-31 17:43:25 +03:00
outln ( " Second thread stole mutex " ) ;
2019-11-16 14:19:58 +03:00
sleep ( 1 ) ;
2021-05-31 17:43:25 +03:00
outln ( " Second thread giving back mutex " ) ;
2019-11-16 14:19:58 +03:00
pthread_mutex_unlock ( & mutex ) ;
sleep ( 1 ) ;
}
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 1 ;
}
for ( ; ; ) {
pthread_mutex_lock ( & mutex ) ;
2021-05-31 17:43:25 +03:00
outln ( " Obnoxious spam! " ) ;
2019-11-16 14:19:58 +03:00
pthread_mutex_unlock ( & mutex ) ;
usleep ( 10000 ) ;
}
}
2019-11-18 06:08:10 +03:00
int detached_test ( )
{
pthread_attr_t attributes ;
int rc = pthread_attr_init ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_init: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 1 ;
}
int detach_state = 99 ; // clearly invalid
rc = pthread_attr_getdetachstate ( & attributes , & detach_state ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_getdetachstate: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 2 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Default detach state: {} " , detach_state = = PTHREAD_CREATE_JOINABLE ? " joinable " : " detached " ) ;
2019-11-18 06:08:10 +03:00
detach_state = PTHREAD_CREATE_DETACHED ;
rc = pthread_attr_setdetachstate ( & attributes , detach_state ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_setdetachstate: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 3 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Set detach state on new thread to detached " ) ;
2019-11-18 06:08:10 +03:00
pthread_t thread_id ;
rc = pthread_create (
& thread_id , & attributes , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2019-11-18 06:08:10 +03:00
sleep ( 1 ) ;
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
2020-12-09 01:51:39 +03:00
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_create: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 4 ;
}
void * ret_val ;
rc = pthread_join ( thread_id , & ret_val ) ;
2020-12-09 01:51:39 +03:00
if ( rc ! = 0 & & rc ! = EINVAL ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_join: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 5 ;
}
2020-12-09 01:51:39 +03:00
if ( rc ! = EINVAL ) {
2021-05-31 17:43:25 +03:00
outln ( " Expected EINVAL! Thread was joinable? " ) ;
2019-11-18 06:08:10 +03:00
return 6 ;
}
sleep ( 2 ) ;
2021-05-31 17:43:25 +03:00
outln ( " Thread was created detached. I sure hope it exited on its own. " ) ;
2019-11-18 06:08:10 +03:00
rc = pthread_attr_destroy ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_destroy: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 7 ;
}
return 0 ;
}
int priority_test ( )
{
pthread_attr_t attributes ;
int rc = pthread_attr_init ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_init: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 1 ;
}
struct sched_param sched_params ;
rc = pthread_attr_getschedparam ( & attributes , & sched_params ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_getschedparam: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 2 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Default priority: {} " , sched_params . sched_priority ) ;
2019-11-18 06:08:10 +03:00
sched_params . sched_priority = 3 ;
rc = pthread_attr_setschedparam ( & attributes , & sched_params ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_setschedparam: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 3 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Set thread priority to 3 " ) ;
2019-11-18 06:08:10 +03:00
pthread_t thread_id ;
rc = pthread_create (
& thread_id , & attributes , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2019-11-18 06:08:10 +03:00
sleep ( 1 ) ;
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 4 ;
}
rc = pthread_join ( thread_id , nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_join " ) ;
return 5 ;
}
rc = pthread_attr_destroy ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_destroy: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 6 ;
}
return 0 ;
}
int stack_size_test ( )
{
pthread_attr_t attributes ;
int rc = pthread_attr_init ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_init: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 1 ;
}
size_t stack_size ;
rc = pthread_attr_getstacksize ( & attributes , & stack_size ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_getstacksize: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 2 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Default stack size: {} " , stack_size ) ;
2019-11-18 06:08:10 +03:00
stack_size = 8 * 1024 * 1024 ;
rc = pthread_attr_setstacksize ( & attributes , stack_size ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_setstacksize: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 3 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Set thread stack size to 8 MiB " ) ;
2019-11-18 06:08:10 +03:00
pthread_t thread_id ;
rc = pthread_create (
& thread_id , & attributes , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2019-11-18 06:08:10 +03:00
sleep ( 1 ) ;
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 4 ;
}
rc = pthread_join ( thread_id , nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_join " ) ;
return 5 ;
}
rc = pthread_attr_destroy ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_destroy: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 6 ;
}
return 0 ;
}
2020-08-09 18:33:13 +03:00
int staying_alive_test ( )
{
pthread_t thread_id ;
int rc = pthread_create (
& thread_id , nullptr , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2020-08-09 18:33:13 +03:00
sleep ( 20 ) ;
2021-05-31 17:43:25 +03:00
outln ( " Secondary thread is still alive " ) ;
2020-08-09 18:33:13 +03:00
sleep ( 3520 ) ;
2021-05-31 17:43:25 +03:00
outln ( " Secondary thread exiting " ) ;
2020-08-09 18:33:13 +03:00
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 1 ;
}
sleep ( 1 ) ;
2021-05-31 17:43:25 +03:00
outln ( " I'm the main thread :^) " ) ;
2020-08-09 18:33:13 +03:00
sleep ( 3600 ) ;
2021-05-31 17:43:25 +03:00
outln ( " Main thread exiting " ) ;
2020-08-09 18:33:13 +03:00
return 0 ;
}
2019-11-18 06:08:10 +03:00
int set_stack_test ( )
{
pthread_attr_t attributes ;
int rc = pthread_attr_init ( & attributes ) ;
if ( rc < 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_init: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 1 ;
}
size_t stack_size = 8 * 1024 * 1024 ;
void * stack_addr = mmap_with_name ( nullptr , stack_size , PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK , 0 , 0 , " Cool stack " ) ;
if ( ! stack_addr ) {
perror ( " mmap_with_name " ) ;
return - 1 ;
}
rc = pthread_attr_setstack ( & attributes , stack_addr , stack_size ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_setstack: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 2 ;
}
2021-05-31 17:43:25 +03:00
outln ( " Set thread stack to {:p}, size {} " , stack_addr , stack_size ) ;
2019-11-18 06:08:10 +03:00
size_t stack_size_verify ;
void * stack_addr_verify ;
rc = pthread_attr_getstack ( & attributes , & stack_addr_verify , & stack_size_verify ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_getstack: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 3 ;
}
if ( stack_addr ! = stack_addr_verify | | stack_size ! = stack_size_verify ) {
2021-05-31 17:43:25 +03:00
outln ( " Stack address and size don't match! addr: {:p} {:p}, size: {} {} " , stack_addr , stack_addr_verify , stack_size , stack_size_verify ) ;
2019-11-18 06:08:10 +03:00
return 4 ;
}
pthread_t thread_id ;
rc = pthread_create (
& thread_id , & attributes , [ ] ( void * ) - > void * {
2021-05-31 17:43:25 +03:00
outln ( " I'm the secondary thread :^) " ) ;
2019-11-18 06:08:10 +03:00
sleep ( 1 ) ;
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 5 ;
}
rc = pthread_join ( thread_id , nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_join " ) ;
return 6 ;
}
rc = pthread_attr_destroy ( & attributes ) ;
if ( rc ! = 0 ) {
2021-05-31 17:43:25 +03:00
outln ( " pthread_attr_destroy: {} " , strerror ( rc ) ) ;
2019-11-18 06:08:10 +03:00
return 7 ;
}
return 0 ;
}
2021-07-09 04:56:29 +03:00
int kill_test ( )
{
pthread_t thread_id ;
int rc = pthread_create (
& thread_id , nullptr , [ ] ( void * ) - > void * {
outln ( " I'm the secondary thread :^) " ) ;
sleep ( 100 ) ;
outln ( " Secondary thread is still alive :^( " ) ;
pthread_exit ( ( void * ) 0xDEADBEEF ) ;
} ,
nullptr ) ;
if ( rc < 0 ) {
perror ( " pthread_create " ) ;
return 1 ;
}
int result = 0 ;
sleep ( 1 ) ;
outln ( " I'm the main thread :^) " ) ;
if ( pthread_kill ( thread_id , 0 ) ! = 0 ) {
perror ( " pthread_kill " ) ;
result = 1 ;
}
if ( pthread_kill ( thread_id , SIGKILL ) ! = 0 ) {
perror ( " pthread_kill(SIGKILL) " ) ;
result = 1 ;
}
outln ( " Main thread exiting " ) ;
return result ;
}