Update async.zsh (#218)
This commit is contained in:
committed by
Mathias Fredriksson
parent
306aa90e54
commit
b3adab82c0
69
async.zsh
69
async.zsh
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# zsh-async
|
# zsh-async
|
||||||
#
|
#
|
||||||
# version: 1.1.0
|
# version: 1.2.0
|
||||||
# author: Mathias Fredriksson
|
# author: Mathias Fredriksson
|
||||||
# url: https://github.com/mafredri/zsh-async
|
# url: https://github.com/mafredri/zsh-async
|
||||||
#
|
#
|
||||||
@@ -43,13 +43,13 @@ _async_job() {
|
|||||||
# have run into a bug
|
# have run into a bug
|
||||||
ret=${ret:--1}
|
ret=${ret:--1}
|
||||||
|
|
||||||
# Grab mutex lock
|
# Grab mutex lock, stalls until token is available
|
||||||
read -ep >/dev/null
|
read -ep >/dev/null
|
||||||
|
|
||||||
# return output (<job_name> <return_code> <stdout> <duration> <stderr>)
|
# return output (<job_name> <return_code> <stdout> <duration> <stderr>)
|
||||||
print -r -N -n -- "$1" "$ret" "$stdout" "$duration" "$stderr"$'\0'
|
print -r -N -n -- "$1" "$ret" "$stdout" "$duration" "$stderr"$'\0'
|
||||||
|
|
||||||
# Unlock mutex
|
# Unlock mutex by inserting a token
|
||||||
print -p "t"
|
print -p "t"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,22 +57,40 @@ _async_job() {
|
|||||||
_async_worker() {
|
_async_worker() {
|
||||||
local -A storage
|
local -A storage
|
||||||
local unique=0
|
local unique=0
|
||||||
|
local notify_parent=0
|
||||||
|
local parent_pid=0
|
||||||
|
local coproc_pid=0
|
||||||
|
|
||||||
|
child_exit() {
|
||||||
|
# If coproc (cat) is the only child running, we close it to avoid
|
||||||
|
# leaving it running indefinitely and cluttering the process tree.
|
||||||
|
if [[ ${#jobstates} = 1 ]] && [[ $coproc_pid = ${${(v)jobstates##*:*:}%\=*} ]]; then
|
||||||
|
coproc :
|
||||||
|
fi
|
||||||
|
|
||||||
|
# On older version of zsh (pre 5.2) we notify the parent through a
|
||||||
|
# SIGWINCH signal because `zpty` did not return a file descriptor (fd)
|
||||||
|
# prior to that.
|
||||||
|
if (( notify_parent )); then
|
||||||
|
# We use SIGWINCH for compatibility with older versions of zsh
|
||||||
|
# (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could
|
||||||
|
# cause a deadlock in the shell under certain circumstances.
|
||||||
|
kill -WINCH $parent_pid
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register a SIGCHLD trap to handle the completion of child processes.
|
||||||
|
trap child_exit CHLD
|
||||||
|
|
||||||
# Process option parameters passed to worker
|
# Process option parameters passed to worker
|
||||||
while getopts "np:u" opt; do
|
while getopts "np:u" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
# Use SIGWINCH since many others seem to cause zsh to freeze, e.g. ALRM, INFO, etc.
|
n) notify_parent=1;;
|
||||||
n) trap 'kill -WINCH $ASYNC_WORKER_PARENT_PID' CHLD;;
|
p) parent_pid=$OPTARG;;
|
||||||
p) ASYNC_WORKER_PARENT_PID=$OPTARG;;
|
|
||||||
u) unique=1;;
|
u) unique=1;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create a mutex for writing to the terminal through coproc
|
|
||||||
coproc cat
|
|
||||||
# Insert token into coproc
|
|
||||||
print -p "t"
|
|
||||||
|
|
||||||
while read -r cmd; do
|
while read -r cmd; do
|
||||||
# Separate on spaces into an array
|
# Separate on spaces into an array
|
||||||
cmd=(${=cmd})
|
cmd=(${=cmd})
|
||||||
@@ -81,7 +99,7 @@ _async_worker() {
|
|||||||
# Check for non-job commands sent to worker
|
# Check for non-job commands sent to worker
|
||||||
case $job in
|
case $job in
|
||||||
_unset_trap)
|
_unset_trap)
|
||||||
trap - CHLD; continue;;
|
notify_parent=0; continue;;
|
||||||
_killjobs)
|
_killjobs)
|
||||||
# Do nothing in the worker when receiving the TERM signal
|
# Do nothing in the worker when receiving the TERM signal
|
||||||
trap '' TERM
|
trap '' TERM
|
||||||
@@ -103,6 +121,16 @@ _async_worker() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Because we close the coproc after the last job has completed, we must
|
||||||
|
# recreate it when there are no other jobs running.
|
||||||
|
if (( !${#jobstates} )); then
|
||||||
|
# Use coproc as a mutex for synchronized output between children.
|
||||||
|
coproc cat
|
||||||
|
coproc_pid=$!
|
||||||
|
# Insert token into coproc
|
||||||
|
print -p "t"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run task in background
|
# Run task in background
|
||||||
_async_job $cmd &
|
_async_job $cmd &
|
||||||
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
|
# Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')...
|
||||||
@@ -130,8 +158,9 @@ async_process_results() {
|
|||||||
integer count=0
|
integer count=0
|
||||||
local worker=$1
|
local worker=$1
|
||||||
local callback=$2
|
local callback=$2
|
||||||
|
local caller=$3
|
||||||
local -a items
|
local -a items
|
||||||
local IFS=$'\0'
|
local line
|
||||||
|
|
||||||
typeset -gA ASYNC_PROCESS_BUFFER
|
typeset -gA ASYNC_PROCESS_BUFFER
|
||||||
# Read output from zpty and parse it if available
|
# Read output from zpty and parse it if available
|
||||||
@@ -139,8 +168,13 @@ async_process_results() {
|
|||||||
# Remove unwanted \r from output
|
# Remove unwanted \r from output
|
||||||
ASYNC_PROCESS_BUFFER[$worker]+=${line//$'\r'$'\n'/$'\n'}
|
ASYNC_PROCESS_BUFFER[$worker]+=${line//$'\r'$'\n'/$'\n'}
|
||||||
# Split buffer on null characters, preserve empty elements
|
# Split buffer on null characters, preserve empty elements
|
||||||
|
# (an anonymous function is used to avoid leaking modified IFS into the callback)
|
||||||
|
() {
|
||||||
|
local IFS=$'\0'
|
||||||
items=("${(@)=ASYNC_PROCESS_BUFFER[$worker]}")
|
items=("${(@)=ASYNC_PROCESS_BUFFER[$worker]}")
|
||||||
# Remove last element since it's due to the return string separator structure
|
}
|
||||||
|
# Remove last element since it's an artifact
|
||||||
|
# of the return string separator structure
|
||||||
items=("${(@)items[1,${#items}-1]}")
|
items=("${(@)items[1,${#items}-1]}")
|
||||||
|
|
||||||
# Continue until we receive all information
|
# Continue until we receive all information
|
||||||
@@ -160,6 +194,9 @@ async_process_results() {
|
|||||||
# If we processed any results, return success
|
# If we processed any results, return success
|
||||||
(( count )) && return 0
|
(( count )) && return 0
|
||||||
|
|
||||||
|
# Avoid printing exit value from setopt printexitvalue
|
||||||
|
[[ $caller = trap || $caller = watcher ]] && return 0
|
||||||
|
|
||||||
# No results were processed
|
# No results were processed
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -172,7 +209,7 @@ _async_zle_watcher() {
|
|||||||
local callback=$ASYNC_CALLBACKS[$worker]
|
local callback=$ASYNC_CALLBACKS[$worker]
|
||||||
|
|
||||||
if [[ -n $callback ]]; then
|
if [[ -n $callback ]]; then
|
||||||
async_process_results $worker $callback
|
async_process_results $worker $callback watcher
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +231,7 @@ _async_notify_trap() {
|
|||||||
setopt localoptions noshwordsplit
|
setopt localoptions noshwordsplit
|
||||||
|
|
||||||
for k in ${(k)ASYNC_CALLBACKS}; do
|
for k in ${(k)ASYNC_CALLBACKS}; do
|
||||||
async_process_results $k ${ASYNC_CALLBACKS[$k]}
|
async_process_results $k ${ASYNC_CALLBACKS[$k]} trap
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user