fix(async): avoid blocking the shell while waiting

Due to "builtin echo $handler", _omz_async_callback was called immediately, as there was data to
read on the pipe. Then the shell was blocked until the end of the async prompt handler.
We now use associative array to get the handler name from the fd.

For an unknown reason, the async child can block when launching a subshell, especially if there are
several handlers registered.
Using a function to set the return code avoids the issue, and is much faster.

Fixes #12290
This commit is contained in:
Loïc Yhuel 2024-03-25 23:19:08 +01:00 committed by Loïc Yhuel
commit 42b22ecf7d

View file

@ -44,7 +44,7 @@ function _omz_register_handler {
# Set up async handlers and callbacks # Set up async handlers and callbacks
function _omz_async_request { function _omz_async_request {
local -i ret=$? local -i ret=$?
typeset -gA _OMZ_ASYNC_FDS _OMZ_ASYNC_PIDS _OMZ_ASYNC_OUTPUT typeset -gA _OMZ_ASYNC_FDS _OMZ_ASYNC_PIDS _OMZ_ASYNC_OUTPUT _OMZ_ASYNC_HANDLERS
# executor runs a subshell for all async requests based on key # executor runs a subshell for all async requests based on key
local handler local handler
@ -82,16 +82,16 @@ function _omz_async_request {
exec {fd}< <( exec {fd}< <(
# Tell parent process our PID # Tell parent process our PID
builtin echo ${sysparams[pid]} builtin echo ${sysparams[pid]}
# Store handler name for callback
builtin echo $handler
# Set exit code for the handler if used # Set exit code for the handler if used
(exit $ret) () { return $ret }
# Run the async function handler # Run the async function handler
$handler $handler
) )
# Save FD for handler # Save FD for handler
_OMZ_ASYNC_FDS[$handler]=$fd _OMZ_ASYNC_FDS[$handler]=$fd
# Save handler name for callback
_OMZ_ASYNC_HANDLERS[$fd]=$handler
# There's a weird bug here where ^C stops working unless we force a fork # There's a weird bug here where ^C stops working unless we force a fork
# See https://github.com/zsh-users/zsh-autosuggestions/issues/364 # See https://github.com/zsh-users/zsh-autosuggestions/issues/364
@ -114,9 +114,8 @@ function _omz_async_callback() {
local err=$2 # Second arg will be passed in case of error local err=$2 # Second arg will be passed in case of error
if [[ -z "$err" || "$err" == "hup" ]]; then if [[ -z "$err" || "$err" == "hup" ]]; then
# Get handler name from first line # Get handler name from fd
local handler local handler=${_OMZ_ASYNC_HANDLERS[$fd]}
read handler <&$fd
# Store old output which is supposed to be already printed # Store old output which is supposed to be already printed
local old_output="${_OMZ_ASYNC_OUTPUT[$handler]}" local old_output="${_OMZ_ASYNC_OUTPUT[$handler]}"
@ -137,6 +136,9 @@ function _omz_async_callback() {
# Always remove the handler # Always remove the handler
zle -F "$fd" zle -F "$fd"
# Remove the fd => handle name association
unset '_OMZ_ASYNC_HANDLERS[$fd]'
# Unset global FD variable to prevent closing user created FDs in the precmd hook # Unset global FD variable to prevent closing user created FDs in the precmd hook
_OMZ_ASYNC_FDS[$handler]=-1 _OMZ_ASYNC_FDS[$handler]=-1
_OMZ_ASYNC_PIDS[$handler]=-1 _OMZ_ASYNC_PIDS[$handler]=-1