Skip to content

Commit

Permalink
app: work around data corruption by WINCH on intermediate state
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Feb 3, 2022
1 parent 88614b8 commit 5065fda
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 60 deletions.
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Expand Up @@ -503,6 +503,7 @@
- complete: fix a problem of redundant unmatched ambiguous part with tilde expansions in the common prefix `#D1417` 5777d7f
- highlight: fix remaininig highlighting of vanishing words `#D1421` `#D1422` 1066653
- complete: fix a problem that the user setting `dotglob` is changed `#D1425` 987436d
- app: work around data corruption by WINCH on intermediate state `#D1762` 0000000

## Compatibility

Expand Down
16 changes: 16 additions & 0 deletions note.txt
Expand Up @@ -5958,6 +5958,22 @@ bash_tips

2022-02-02

* render: 処理途中に WINCH が来ると途中状態のデータに対して処理が走ってデータが壊れる [#D1762]

以下のテスト中に描画の無限ループを検出した。
https://github.com/akinomyoga/ble.sh/issues/173#issuecomment-1028479357

更に色々やっていると tree_node に inconsistency が発生した。というところで
分かった。恐らく描画処理の途中で SIGWINCH が走った所為で、中途半端な状態で
新しい描画が始まる事によってデータを破壊している。処理の途中で描画は実行し
ない様にする必要がある。

特に PROLOGUE と EPILOGUE の間で SIGWINCH が来た時には EPILOGUE の末尾で改
めて再描画を実行する様にする必要があるのではないか。

後、描画処理が終わった後のサイズ変更検知も実行するべきなのかもしれない。と
思ったが、それは実は通常の描画の際にも毎回実行するべき事なのではないか。

* mandb: rsync --no-while-f[TAB] で説明が一緒に挿入されてしまう [#D1761]

DATA を挿入しているのだろうか。。或いは、単一確定の場合に postprocess が走っ
Expand Down
4 changes: 4 additions & 0 deletions src/decode.sh
Expand Up @@ -638,6 +638,7 @@ elif ((_ble_bash>=40000)); then
ble/function#suppress-stderr ble/decode/nonblocking-read
fi

_ble_decode_hook_Processing=
function ble-decode/.hook {
if ble/util/is-stdin-ready; then
ble/array#push _ble_decode_input_buffer "$@"
Expand Down Expand Up @@ -675,7 +676,9 @@ function ble-decode/.hook {
[[ $_ble_bash_options_adjusted ]] && set +v || :

local IFS=$_ble_term_IFS
local _ble_decode_hook_Processing=prologue
ble-decode/PROLOGUE
_ble_decode_hook_Processing=body

# abort #D0998
if ble-decode/.check-abort "$1"; then
Expand Down Expand Up @@ -722,6 +725,7 @@ function ble-decode/.hook {
ble/decode/has-input || ble-decode-key/batch/flush

builtin eval -- "$_ble_decode_erase_progress_hook"
_ble_decode_hook_Processing=epilogue
ble-decode/EPILOGUE
}

Expand Down
156 changes: 96 additions & 60 deletions src/edit.sh
Expand Up @@ -253,6 +253,7 @@ bleopt/declare -v line_limit_type none

_ble_app_render_mode=panel
_ble_app_winsize=()
_ble_app_render_Processing=
function ble/application/.set-up-render-mode {
[[ $1 == "$_ble_app_render_mode" ]] && return 0
case $1 in
Expand All @@ -279,18 +280,86 @@ function ble/application/pop-render-mode {
ble/array#shift _ble_app_render_mode
}
function ble/application/render {
local render=$_ble_app_render_mode
case $render in
(panel)
local _ble_prompt_update=owner
ble/prompt/update
ble/edit/leave-command-layout # ble/edit 特有
ble/canvas/panel/render ;;
(forms:*)
ble/forms/render "${render#*:}" ;;
esac
_ble_app_winsize=("$COLUMNS" "$LINES")
ble/util/buffer.flush >&2
local _ble_app_render_Processing=1
{
local render=$_ble_app_render_mode
case $render in
(panel)
local _ble_prompt_update=owner
ble/prompt/update
ble/edit/leave-command-layout # ble/edit 特有
ble/canvas/panel/render ;;
(forms:*)
ble/forms/render "${render#*:}" ;;
esac
_ble_app_winsize=("$COLUMNS" "$LINES")
ble/util/buffer.flush >&2
}
ble/util/unlocal _ble_app_render_Processing
if [[ $_ble_application_render_winch ]]; then
_ble_application_render_winch=
ble/application/onwinch
fi
}

function ble/application/onwinch {
if [[ $_ble_app_render_Processing || $_ble_decode_hook_Processing == body || $_ble_decode_hook_Processing == prologue ]]; then
# Note #D1762: 別の処理が走っている途中に描画更新すると中途半端な
# データに対して処理が実行されてデータが破壊されるので後で処理する。
#
# ble_decode_hook_body=1 の時は EPILOGUE が後で必ず呼び出されるの
# でその時に ble/application/render が呼び出される。その中で
# _ble_application_render_winch がチェックされて改めてこの関数が呼
# び出される。_ble_app_render_Processing=1 の時には、
# ble/application/render の末尾でやはりチェックが走ると期待する。
_ble_application_render_winch=1
return 0
fi

_ble_textmap_pos=()
# 処理中に届いた WINCH は失われる様だ。連続的サイズ変化を通知す
# る端末の場合、途中のサイズの WINCH の処理中に最終的なサイズの
# WINCH を逃して表示が乱れたままになる。対策として描画終了時に処
# 理中にサイズ変化が起こっていないか確認する。

local old_size= i
for ((i=0;i<20;i++)); do
# 次の待つと共にサブシェルで checkwinsize を誘発
(ble/util/msleep 50)
# trap 中だと bash のバグでジョブが溜まるので逐次捌く
ble/util/joblist.check ignore-volatile-jobs
local size=$LINES:$COLUMNS
[[ $size == "$old_size" ]] && break

local _ble_app_render_Processing=1
{
old_size=$size

case $bleopt_canvas_winch_action in
(clear)
# 全消去して一番上から再描画
ble/util/buffer "$_ble_term_clear" ;;
(redraw-here)
# 現在位置から再描画 (幅が減った時は前のコマンドの出力結果を破壊しな
# いので戻る)
if ((COLUMNS<_ble_app_winsize[0])); then
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw
fi ;;
(redraw-prev)
# 前回の開始相対位置が変化していないと仮定して戻って再描画
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw ;;
esac

ble/canvas/panel/invalidate height # 高さの再確保も含めて。
}
ble/util/unlocal _ble_app_render_Processing

ble/application/render
done
}

# canvas.sh 設定
Expand Down Expand Up @@ -529,6 +598,16 @@ function ble/prompt/unit#update/.update-dependencies {
if [[ $otree ]]; then
ble/string#split otree , "$otree"

if [[ ! $ble_prompt_unit_processing ]]; then
local ble_prompt_unit_processing=1
"${_ble_util_set_declare[@]//NAME/ble_prompt_unit_mark}" # WA #D1570
elif ble/set#contains ble_prompt_unit_mark "$unit"; then
ble/util/print "ble/prompt: FATAL: detected cyclic dependency ($unit required by $ble_prompt_unit_parent)" >/dev/tty
return 1
fi
local ble_prompt_unit_parent=$unit
ble/set#add ble_prompt_unit_mark "$unit"

local prompt_unit= # 依存関係の登録はしない
local child
for child in "${otree[@]}"; do
Expand All @@ -537,6 +616,8 @@ function ble/prompt/unit#update/.update-dependencies {
ble/is-function ble/prompt/unit:"$child"/update &&
ble/prompt/unit#update "$child"
done

ble/set#remove ble_prompt_unit_mark "$unit"
fi
}
function ble/prompt/unit#clear {
Expand Down Expand Up @@ -2344,54 +2425,9 @@ function ble-edit/eval-IGNOREEOF {
bleopt/declare -n canvas_winch_action redraw-here

function ble-edit/attach/TRAPWINCH {
local FUNCNEST=
local IFS=$_ble_term_IFS
if ((_ble_edit_attached)); then
if [[ $_ble_term_state == internal ]]; then
_ble_textmap_pos=()
ble-edit/bind/stdout.on

# 処理中に届いた WINCH は失われる様だ。連続的サイズ変化を通知す
# る端末の場合、途中のサイズの WINCH の処理中に最終的なサイズの
# WINCH を逃して表示が乱れたままになる。対策として描画終了時に処
# 理中にサイズ変化が起こっていないか確認する。

local old_size= i
for ((i=0;i<20;i++)); do
# 次の待つと共にサブシェルで checkwinsize を誘発
(ble/util/msleep 50)
# trap 中だと bash のバグでジョブが溜まるので逐次捌く
ble/util/joblist.check ignore-volatile-jobs
local size=$LINES:$COLUMNS
[[ $size == "$old_size" ]] && break
old_size=$size

case $bleopt_canvas_winch_action in
(clear)
# 全消去して一番上から再描画
ble/util/buffer "$_ble_term_clear" ;;
(redraw-here)
# 現在位置から再描画 (幅が減った時は前のコマンドの出力結果を破壊しな
# いので戻る)
if ((COLUMNS<_ble_app_winsize[0])); then
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw
fi ;;
(redraw-prev)
# 前回の開始相対位置が変化していないと仮定して戻って再描画
local -a DRAW_BUFF=()
ble/canvas/panel#goto.draw 0 0 0
ble/canvas/bflush.draw ;;
esac

ble/canvas/panel/invalidate height # 高さの再確保も含めて。
ble/application/render
done

ble-edit/bind/stdout.off
fi
fi
# 現在前面に出ていなければ関係ない
((_ble_edit_attached)) && [[ $_ble_term_state == internal ]] || return 0
ble/application/onwinch 2>&$_ble_util_fd_stderr
}

## called by ble-edit/attach
Expand Down
1 change: 1 addition & 0 deletions src/util.sh
Expand Up @@ -2149,6 +2149,7 @@ function ble/builtin/trap/invoke {
function ble/builtin/trap/.handler {
local _ble_trap_lastexit=$? _ble_trap_lastarg=$_ FUNCNEST=
local _ble_trap_sig=$1 _ble_trap_name=$2
local FUNCNEST= IFS=$_ble_term_IFS
local set shopt; ble/base/.adjust-bash-options set shopt

# 透過 _ble_builtin_trap_postproc を設定
Expand Down

0 comments on commit 5065fda

Please sign in to comment.