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
|
||||
#
|
||||
# version: 1.1.0
|
||||
# version: 1.2.0
|
||||
# author: Mathias Fredriksson
|
||||
# url: https://github.com/mafredri/zsh-async
|
||||
#
|
||||
@@ -43,13 +43,13 @@ _async_job() {
|
||||
# have run into a bug
|
||||
ret=${ret:--1}
|
||||
|
||||
# Grab mutex lock
|
||||
# Grab mutex lock, stalls until token is available
|
||||
read -ep >/dev/null
|
||||
|
||||
# return output (<job_name> <return_code> <stdout> <duration> <stderr>)
|
||||
print -r -N -n -- "$1" "$ret" "$stdout" "$duration" "$stderr"$'\0'
|
||||
|
||||
# Unlock mutex
|
||||
# Unlock mutex by inserting a token
|
||||
print -p "t"
|
||||
}
|
||||
|
||||
@@ -57,22 +57,40 @@ _async_job() {
|
||||
_async_worker() {
|
||||
local -A storage
|
||||
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
|
||||
while getopts "np:u" opt; do
|
||||
case $opt in
|
||||
# Use SIGWINCH since many others seem to cause zsh to freeze, e.g. ALRM, INFO, etc.
|
||||
n) trap 'kill -WINCH $ASYNC_WORKER_PARENT_PID' CHLD;;
|
||||
p) ASYNC_WORKER_PARENT_PID=$OPTARG;;
|
||||
n) notify_parent=1;;
|
||||
p) parent_pid=$OPTARG;;
|
||||
u) unique=1;;
|
||||
esac
|
||||
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
|
||||
# Separate on spaces into an array
|
||||
cmd=(${=cmd})
|
||||
@@ -81,7 +99,7 @@ _async_worker() {
|
||||
# Check for non-job commands sent to worker
|
||||
case $job in
|
||||
_unset_trap)
|
||||
trap - CHLD; continue;;
|
||||
notify_parent=0; continue;;
|
||||
_killjobs)
|
||||
# Do nothing in the worker when receiving the TERM signal
|
||||
trap '' TERM
|
||||
@@ -103,6 +121,16 @@ _async_worker() {
|
||||
done
|
||||
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
|
||||
_async_job $cmd &
|
||||
# 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
|
||||
local worker=$1
|
||||
local callback=$2
|
||||
local caller=$3
|
||||
local -a items
|
||||
local IFS=$'\0'
|
||||
local line
|
||||
|
||||
typeset -gA ASYNC_PROCESS_BUFFER
|
||||
# Read output from zpty and parse it if available
|
||||
@@ -139,8 +168,13 @@ async_process_results() {
|
||||
# Remove unwanted \r from output
|
||||
ASYNC_PROCESS_BUFFER[$worker]+=${line//$'\r'$'\n'/$'\n'}
|
||||
# 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]}")
|
||||
# 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]}")
|
||||
|
||||
# Continue until we receive all information
|
||||
@@ -160,6 +194,9 @@ async_process_results() {
|
||||
# If we processed any results, return success
|
||||
(( count )) && return 0
|
||||
|
||||
# Avoid printing exit value from setopt printexitvalue
|
||||
[[ $caller = trap || $caller = watcher ]] && return 0
|
||||
|
||||
# No results were processed
|
||||
return 1
|
||||
}
|
||||
@@ -172,7 +209,7 @@ _async_zle_watcher() {
|
||||
local callback=$ASYNC_CALLBACKS[$worker]
|
||||
|
||||
if [[ -n $callback ]]; then
|
||||
async_process_results $worker $callback
|
||||
async_process_results $worker $callback watcher
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -194,7 +231,7 @@ _async_notify_trap() {
|
||||
setopt localoptions noshwordsplit
|
||||
|
||||
for k in ${(k)ASYNC_CALLBACKS}; do
|
||||
async_process_results $k ${ASYNC_CALLBACKS[$k]}
|
||||
async_process_results $k ${ASYNC_CALLBACKS[$k]} trap
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user