-
Couldn't load subscription status.
- Fork 3k
kernel: Update standard_error to use prim_tty nif #9116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
kernel: Update standard_error to use prim_tty nif #9116
Conversation
CT Test Results 4 files 200 suites 1h 51m 51s ⏱️ For more details on these failures, see this check. Results for commit ba6446b. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
dd4251d to
ba6446b
Compare
ba6446b to
d79b98a
Compare
standard_error is started before the on_load handlers in init are run, so prim_tty will not be completely loaded when running in embedded mode. So we call it during init of prim_tty instead.
The primary reason that we do this is to make writing to standard_error as synchronous as possible. When using the fd driver the write was done on an async thread, which meant that there was no simple way to get an acknowledgement when the write had completed. Now that we use a NIF we can much more easily get a check when the write is done so that we know when io:format(standard_error, ...) has returned we have also written to that fd.
d79b98a to
ead7424
Compare
Fix a race condition where the standard_error process could call prim_tty:init/1 before being registered, causing a crash when prim_tty's writer and reader processes attempt to derive their own registered names from the parent process. The issue was introduced in commit ead7424 (PR erlang#9116), which changed prim_tty's reader and writer processes to dynamically derive their registered names from their parent process name (e.g., 'standard_error' -> 'standard_error_reader', 'standard_error_writer'). This was done to support multiple TTY instances (stdout and stderr) with distinct process names. However, the standard_error:start/0 function had a race condition: 1. spawn(fun server/0) - creates process, starts executing immediately 2. register(?NAME, Id) - parent registers the spawned process 3. server() calls prim_tty:init() - may execute before step 2 When prim_tty:init/1 spawns writer/reader processes, they call set_name(Parent, Postfix) which does: erlang:process_info(Parent, registered_name) If this executes before the parent completes registration, it returns [] instead of {registered_name, standard_error}, causing a pattern match failure: "no match of right hand side value: []" at prim_tty.erl:674. The fix moves the register/2 call into the spawned process itself, ensuring it completes before prim_tty:init/1 is called. This eliminates the race condition entirely. Closes erlang#10174
Fix a race condition where the standard_error process could call prim_tty:init/1 before being registered, causing a crash when prim_tty's writer and reader processes attempt to derive their own registered names from the parent process. The issue was introduced in commit ead7424 (PR erlang#9116), which changed prim_tty's reader and writer processes to dynamically derive their registered names from their parent process name (e.g., 'standard_error' -> 'standard_error_reader', 'standard_error_writer'). This was done to support multiple TTY instances (stdout and stderr) with distinct process names. However, the standard_error:start/0 function had a race condition: 1. spawn(fun server/0) - creates process, starts executing immediately 2. register(?NAME, Id) - parent registers the spawned process 3. server() calls prim_tty:init() - may execute before step 2 When prim_tty:init/1 spawns writer/reader processes, they call set_name(Parent, Postfix) which does: erlang:process_info(Parent, registered_name) If this executes before the parent completes registration, it returns [] instead of {registered_name, standard_error}, causing a pattern match failure: "no match of right hand side value: []" at prim_tty.erl:674. The fix moves the register/2 call into the spawned process itself, ensuring it completes before prim_tty:init/1 is called. This eliminates the race condition entirely. Closes erlang#10174
The primary reason that we do this is to make writing to standard_error as synchronous as possible. When using the fd driver the write was done on an async thread, which meant that there was no simple way to get an acknowledgement when the write had completed. Now that we use a NIF we can much more easily get a check when the write is done so that we know when io:format(standard_error, ...) has returned we have also written to that fd.