From b0d845f809c9b4988ec9ffa98da598089a17dd79 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 5 Dec 2024 19:25:05 +0100 Subject: [PATCH 1/2] Explicit exit from main In a MT environment the main thread's fiber may be resumed by any thread, which may terminate the program, and would return from another thread that the process' main thread, which may be This patches instead explicitly exits from `main` and `wmain`. For backward compatibility reason (win32's `wmain` and wasi's `__main_argc_argv` call `main`), the default `main` still returns, and is replaced for UNIX targets. --- src/crystal/main.cr | 6 +++--- src/crystal/system/unix/main.cr | 11 +++++++++++ src/crystal/system/wasi/main.cr | 3 ++- src/crystal/system/win32/wmain.cr | 24 +++++++++++++----------- 4 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 src/crystal/system/unix/main.cr diff --git a/src/crystal/main.cr b/src/crystal/main.cr index 625238229c58..9b4384f16a8c 100644 --- a/src/crystal/main.cr +++ b/src/crystal/main.cr @@ -132,8 +132,8 @@ end {% if flag?(:win32) %} require "./system/win32/wmain" -{% end %} - -{% if flag?(:wasi) %} +{% elsif flag?(:wasi) %} require "./system/wasi/main" +{% else %} + require "./system/unix/main" {% end %} diff --git a/src/crystal/system/unix/main.cr b/src/crystal/system/unix/main.cr new file mode 100644 index 000000000000..1592a6342002 --- /dev/null +++ b/src/crystal/system/unix/main.cr @@ -0,0 +1,11 @@ +require "c/stdlib" + +# Prefer explicit exit over returning the status, so we are free to resume the +# main thread's fiber on any thread, without occuring a weird behavior where +# another thread returns from main when the caller might expect the main thread +# to be the one returning. + +fun main(argc : Int32, argv : UInt8**) : Int32 + status = Crystal.main(argc, argv) + LibC.exit(status) +end diff --git a/src/crystal/system/wasi/main.cr b/src/crystal/system/wasi/main.cr index 57ffd5f3f43c..d4bd2a2762c0 100644 --- a/src/crystal/system/wasi/main.cr +++ b/src/crystal/system/wasi/main.cr @@ -27,7 +27,8 @@ fun _start LibWasi.proc_exit(status) if status != 0 end -# `__main_argc_argv` is called by wasi-libc's `__main_void` with the program arguments. +# `__main_argc_argv` is called by wasi-libc's `__main_void` with the program +# arguments. Invokes `Crystal.main`. fun __main_argc_argv(argc : Int32, argv : UInt8**) : Int32 main(argc, argv) end diff --git a/src/crystal/system/win32/wmain.cr b/src/crystal/system/win32/wmain.cr index 2120bfc06bfc..c50c0aa3895e 100644 --- a/src/crystal/system/win32/wmain.cr +++ b/src/crystal/system/win32/wmain.cr @@ -12,17 +12,15 @@ require "c/stdlib" lib LibCrystalMain end -# The actual entry point for Windows executables. This is necessary because -# *argv* (and Win32's `GetCommandLineA`) mistranslate non-ASCII characters to -# Windows-1252, so `PROGRAM_NAME` and `ARGV` would be garbled; to avoid that, we -# use this Windows-exclusive entry point which contains the correctly encoded -# UTF-16 *argv*, convert it to UTF-8, and then forward it to the original -# `main`. +# The actual entry point for Windows executables. # -# The different main functions in `src/crystal/main.cr` need not be aware that -# such an alternate entry point exists, nor that the original command line was -# not UTF-8. Thus all other aspects of program initialization still occur there, -# and uses of those main functions continue to work across platforms. +# This is necessary because *argv* (and Win32's `GetCommandLineA`) mistranslate +# non-ASCII characters to Windows-1252, so `PROGRAM_NAME` and `ARGV` would be +# garbled; to avoid that, we use this Windows-exclusive entry point which +# contains the correctly encoded UTF-16 *argv*, convert it to UTF-8, and then +# forward it to the original `main`. +# +# Invokes `Crystal.main`. # # NOTE: we cannot use anything from the standard library here, including the GC. fun wmain(argc : Int32, argv : UInt16**) : Int32 @@ -46,5 +44,9 @@ fun wmain(argc : Int32, argv : UInt16**) : Int32 end LibC.free(utf8_argv) - status + # prefer explicit exit over returning the status, so we are free to resume the + # main thread's fiber on any thread, without occuring a weird behavior where + # another thread returns from main when the caller might expect the main + # thread to be the one returning. + LibC.exit(status) end From 5d4ef1afb17fe593865e9278e2ed3fa3cb2c6d21 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 20 Dec 2024 19:51:26 +0100 Subject: [PATCH 2/2] Apply suggestions from code review --- src/crystal/system/wasi/main.cr | 2 +- src/crystal/system/win32/wmain.cr | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/crystal/system/wasi/main.cr b/src/crystal/system/wasi/main.cr index d4bd2a2762c0..9a3394809271 100644 --- a/src/crystal/system/wasi/main.cr +++ b/src/crystal/system/wasi/main.cr @@ -28,7 +28,7 @@ fun _start end # `__main_argc_argv` is called by wasi-libc's `__main_void` with the program -# arguments. Invokes `Crystal.main`. +# arguments. fun __main_argc_argv(argc : Int32, argv : UInt8**) : Int32 main(argc, argv) end diff --git a/src/crystal/system/win32/wmain.cr b/src/crystal/system/win32/wmain.cr index c50c0aa3895e..b1726f90329b 100644 --- a/src/crystal/system/win32/wmain.cr +++ b/src/crystal/system/win32/wmain.cr @@ -20,8 +20,6 @@ end # contains the correctly encoded UTF-16 *argv*, convert it to UTF-8, and then # forward it to the original `main`. # -# Invokes `Crystal.main`. -# # NOTE: we cannot use anything from the standard library here, including the GC. fun wmain(argc : Int32, argv : UInt16**) : Int32 utf8_argv = LibC.malloc(sizeof(UInt8*) &* argc).as(UInt8**)