Skip to content

Commit c38f4df

Browse files
authored
Explicit exit from main (#15299)
In a MT environment such as proposed in crystal-lang/rfcs#2, the main thread's fiber may be resumed by any thread, and it may return which would terminate the program... but it might return from _another thread_ that the process' main thread, which may be unexpected by the OS. This patch instead explicitly exits from `main` and `wmain`. For backward compatibility reasons (win32 `wmain` and wasi `__main_argc_argv` both call `main` andand are documented to do so), the default `main` still returns, but is being replaced for UNIX targets by one that exits. Maybe the OS actual entrypoint could merely call `Crystal.main` instead of `main` and explicitely exit (there wouldn't be a global `main` except for `UNIX`), but this is out of scope for this PR.
1 parent d21008e commit c38f4df

File tree

4 files changed

+27
-15
lines changed

4 files changed

+27
-15
lines changed

src/crystal/main.cr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ end
132132

133133
{% if flag?(:win32) %}
134134
require "./system/win32/wmain"
135-
{% end %}
136-
137-
{% if flag?(:wasi) %}
135+
{% elsif flag?(:wasi) %}
138136
require "./system/wasi/main"
137+
{% else %}
138+
require "./system/unix/main"
139139
{% end %}

src/crystal/system/unix/main.cr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require "c/stdlib"
2+
3+
# Prefer explicit exit over returning the status, so we are free to resume the
4+
# main thread's fiber on any thread, without occuring a weird behavior where
5+
# another thread returns from main when the caller might expect the main thread
6+
# to be the one returning.
7+
8+
fun main(argc : Int32, argv : UInt8**) : Int32
9+
status = Crystal.main(argc, argv)
10+
LibC.exit(status)
11+
end

src/crystal/system/wasi/main.cr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ fun _start
2727
LibWasi.proc_exit(status) if status != 0
2828
end
2929

30-
# `__main_argc_argv` is called by wasi-libc's `__main_void` with the program arguments.
30+
# `__main_argc_argv` is called by wasi-libc's `__main_void` with the program
31+
# arguments.
3132
fun __main_argc_argv(argc : Int32, argv : UInt8**) : Int32
3233
main(argc, argv)
3334
end

src/crystal/system/win32/wmain.cr

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,13 @@ require "c/stdlib"
1212
lib LibCrystalMain
1313
end
1414

15-
# The actual entry point for Windows executables. This is necessary because
16-
# *argv* (and Win32's `GetCommandLineA`) mistranslate non-ASCII characters to
17-
# Windows-1252, so `PROGRAM_NAME` and `ARGV` would be garbled; to avoid that, we
18-
# use this Windows-exclusive entry point which contains the correctly encoded
19-
# UTF-16 *argv*, convert it to UTF-8, and then forward it to the original
20-
# `main`.
15+
# The actual entry point for Windows executables.
2116
#
22-
# The different main functions in `src/crystal/main.cr` need not be aware that
23-
# such an alternate entry point exists, nor that the original command line was
24-
# not UTF-8. Thus all other aspects of program initialization still occur there,
25-
# and uses of those main functions continue to work across platforms.
17+
# This is necessary because *argv* (and Win32's `GetCommandLineA`) mistranslate
18+
# non-ASCII characters to Windows-1252, so `PROGRAM_NAME` and `ARGV` would be
19+
# garbled; to avoid that, we use this Windows-exclusive entry point which
20+
# contains the correctly encoded UTF-16 *argv*, convert it to UTF-8, and then
21+
# forward it to the original `main`.
2622
#
2723
# NOTE: we cannot use anything from the standard library here, including the GC.
2824
fun wmain(argc : Int32, argv : UInt16**) : Int32
@@ -46,5 +42,9 @@ fun wmain(argc : Int32, argv : UInt16**) : Int32
4642
end
4743
LibC.free(utf8_argv)
4844

49-
status
45+
# prefer explicit exit over returning the status, so we are free to resume the
46+
# main thread's fiber on any thread, without occuring a weird behavior where
47+
# another thread returns from main when the caller might expect the main
48+
# thread to be the one returning.
49+
LibC.exit(status)
5050
end

0 commit comments

Comments
 (0)