diff --git a/GNUmakefile b/GNUmakefile index 4325f3fe..8af87045 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -77,6 +77,12 @@ $(OUTDIR)/lib/%.txt: lib/%.txt | $(OUTDIR)/lib cp -p $< $@ $(OUTDIR)/lib/core-syntax.sh: lib/core-syntax.sh lib/core-syntax-ctx.def | $(OUTDIR)/lib $(MWGPP) $< > $@ +$(OUTDIR)/lib/init-msys1.sh: lib/init-msys1.sh lib/init-msys1-helper.c | $(OUTDIR)/lib + $(MWGPP) $< > $@ + +#outfiles += $(OUTDIR)/lib/init-msleep.sh +#$(OUTDIR)/lib/init-msleep.sh: lib/init-msleep.sh lib/init-msleep.c | $(OUTDIR)/lib +# $(MWGPP) $< > $@ #------------------------------------------------------------------------------ # contrib diff --git a/ble.pp b/ble.pp index 63f5318d..14baec9c 100644 --- a/ble.pp +++ b/ble.pp @@ -306,7 +306,7 @@ function ble/bin/.freeze-utility-path { # POSIX utilities -_ble_init_posix_command_list=(sed date rm mkdir mkfifo sleep stty tty sort awk chmod grep cat wc mv sh od) +_ble_init_posix_command_list=(sed date rm mkdir mkfifo sleep stty tty sort awk chmod grep cat wc mv sh od cp) function ble/.check-environment { if ! ble/bin#has "${_ble_init_posix_command_list[@]}"; then local cmd commandMissing= @@ -862,6 +862,7 @@ function ble/base/unload { ble-decode/keymap/unload ble-edit/bind/clear-keymap-definition-loader ble/bin/rm -rf "$_ble_base_run/$$".* 2>/dev/null + blehook/invoke unload return 0 } blehook EXIT+=ble/base/unload diff --git a/lib/init-msleep.c b/lib/init-msleep.c new file mode 100644 index 00000000..15abb57d --- /dev/null +++ b/lib/init-msleep.c @@ -0,0 +1,32 @@ +// For Cygwin and MSYS +#include +#include +#include +#include +#include + +#define BUILTIN_ENABLED 0x01 +struct word_desc { char* word; int flags; }; +struct word_list { struct word_list* next; struct word_desc* word; }; +struct builtin { + const char* name; + int (*function)(struct word_list*); + int flags; + const char** long_doc; + const char* short_doc; + char* handle; +}; + +static int msleep_builtin(struct word_list* list) { + if (!list || !list->word) return 2; + double value = atof(list->word->word) * 0.001; + if (value < 0.0) return 2; + if (value == 0.0) return 0; + struct timespec tv; + tv.tv_sec = floor(value); + tv.tv_nsec = floor((value - floor(value)) * 1e9); + while (nanosleep(&tv, &tv) == -1 && errno == EINTR); + return 0; +} +static const char* msleep_doc[] = { "This is a builtin for ble.sh. Sleep for 'msec' milliseconds.", 0 }; +struct builtin msleep_struct = { "ble/builtin/msleep", msleep_builtin, BUILTIN_ENABLED, msleep_doc, "ble/builtin/msleep msec", 0, }; diff --git a/lib/init-msleep.sh b/lib/init-msleep.sh new file mode 100644 index 00000000..9b8e161f --- /dev/null +++ b/lib/init-msleep.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +function ble/util/msleep/.use-compiled-builtin/compile { + local builtin_path=$1 + [[ -x $builtin_path && $builtin_path -nt $_ble_base/lib/init-msleep.sh ]] && return 0 + + local CC=cc + ble/bin#has gcc && CC=gcc + + local include='#include' # '#' で始まる行はインストール時に消される + "$CC" -O2 -s -shared -o "$builtin_path" -xc - << EOF || return 1 +#%$ sed 's/^#include/$include/' lib/init-msleep.c +EOF + [[ -x $builtin_path ]] +} &>/dev/null + +function ble/util/msleep/.use-compiled-builtin { + local basename=$_ble_edit_io_fname2 + local fname_buff=$basename.buff + + local builtin_path=$_ble_base_cache/$HOSTNAME.init-msleep.so + local builtin_runpath=$_ble_base_run/$$.init-msleep.so + ble/util/msleep/.use-compiled-builtin/compile "$builtin_path" && + ble/bin/cp "$builtin_path" "$builtin_runpath" || return 1 + + enable -f "$builtin_runpath" msleep || return 1 + blehook unload+='enable -d ble/builtin/msleep &>/dev/null' + function ble/util/msleep { ble/builtin/msleep "$1"; } +} diff --git a/lib/init-msys1-helper.c b/lib/init-msys1-helper.c new file mode 100644 index 00000000..05e05e56 --- /dev/null +++ b/lib/init-msys1-helper.c @@ -0,0 +1,75 @@ +// For MSYS 1.0 +#include +#include +#include +#include + +BOOL is_process_alive(HANDLE handle) { + DWORD result; + return GetExitCodeProcess(handle, &result) && result == STILL_ACTIVE; +} + +BOOL is_file(const char* filename) { + struct stat st; + return stat(filename, &st) == 0 && S_ISREG(st.st_mode); +} + +int main(int argc, char** argv) { + const char* winpid = argv[1]; + const char* fname_buff = argv[2]; + const char* fname_read = argv[3]; + + signal(SIGINT, SIG_IGN); + //signal(SIGQUIT, SIG_IGN); + + int ppid = atoi(winpid); + if (!ppid) { + fprintf(stderr, "ble.sh (msys1): invalid process ID '%s'\n", winpid); + return 1; + } + HANDLE parent_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ppid); + if (parent_process == NULL) { + fprintf(stderr, "ble.sh (msys1): failed to open the parent process '%s'\n", winpid); + return 1; + } + + int exit_code = 0; + BOOL terminate = FALSE; + while (!terminate) { + unlink(fname_read); + if (rename(fname_buff, fname_read) != 0) { + perror("ble.sh (msys1)"); + fprintf(stderr, "ble.sh (msys1): failed to move the file '%s' -> '%s'\n", fname_buff, fname_read); + terminate = TRUE; + exit_code = 1; + break; + } + + FILE* f = fopen(fname_read, "r"); + if (!f) { + fprintf(stderr, "ble.sh (msys1): failed to open the file '%s'\n", fname_read); + terminate = TRUE; + exit_code = 1; + break; + } + + for (;;) { + if (!is_process_alive(parent_process)) { + terminate = TRUE; + break; + } + if (is_file(fname_buff)) break; + + int count = 0; + char buff[4096]; + while (count = fread(&buff, 1, sizeof buff, f)) + fwrite(buff, 1, count, stdout); + fflush(stdout); + Sleep(20); + } + fclose(f); + } + + CloseHandle(parent_process); + return exit_code; +} diff --git a/lib/init-msys1.sh b/lib/init-msys1.sh index 76c00430..b0ea2e8b 100644 --- a/lib/init-msys1.sh +++ b/lib/init-msys1.sh @@ -39,80 +39,7 @@ function ble-edit/io:msys1/compile-helper { # /mingw/bin/gcc local include='#include' # '#' で始まる行はインストール時に消される gcc -O2 -s -o "$helper" -xc - << EOF || return 1 -$include -$include -$include -$include - -BOOL is_process_alive(HANDLE handle) { - DWORD result; - return GetExitCodeProcess(handle, &result) && result == STILL_ACTIVE; -} - -BOOL is_file(const char* filename) { - struct stat st; - return stat(filename, &st) == 0 && S_ISREG(st.st_mode); -} - -int main(int argc, char** argv) { - const char* winpid = argv[1]; - const char* fname_buff = argv[2]; - const char* fname_read = argv[3]; - - signal(SIGINT, SIG_IGN); - //signal(SIGQUIT, SIG_IGN); - - int ppid = atoi(winpid); - if (!ppid) { - fprintf(stderr, "ble.sh (msys1): invalid process ID '%s'\n", winpid); - return 1; - } - HANDLE parent_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ppid); - if (parent_process == NULL) { - fprintf(stderr, "ble.sh (msys1): failed to open the parent process '%s'\n", winpid); - return 1; - } - - int exit_code = 0; - BOOL terminate = FALSE; - while (!terminate) { - unlink(fname_read); - if (rename(fname_buff, fname_read) != 0) { - perror("ble.sh (msys1)"); - fprintf(stderr, "ble.sh (msys1): failed to move the file '%s' -> '%s'\n", fname_buff, fname_read); - terminate = TRUE; - exit_code = 1; - break; - } - - FILE* f = fopen(fname_read, "r"); - if (!f) { - fprintf(stderr, "ble.sh (msys1): failed to open the file '%s'\n", fname_read); - terminate = TRUE; - exit_code = 1; - break; - } - - for (;;) { - if (!is_process_alive(parent_process)) { - terminate = TRUE; - break; - } - if (is_file(fname_buff)) break; - - int count = 0; - char buff[4096]; - while (count = fread(&buff, 1, sizeof buff, f)) - fwrite(buff, 1, count, stdout); - fflush(stdout); - Sleep(20); - } - fclose(f); - } - - CloseHandle(parent_process); - return exit_code; -} +#%$ sed 's/^#include/$include/' lib/init-msys1-helper.c EOF [[ -x $helper ]] diff --git a/memo/ChangeLog.md b/memo/ChangeLog.md index 360e6944..91695637 100644 --- a/memo/ChangeLog.md +++ b/memo/ChangeLog.md @@ -33,6 +33,7 @@ - edit (sword): fix definition of `sword` (shell words) `#D1441` f923388 - edit (`kill-forward-logical-line`): fix a bug not deleting newline at the end of the line `#D1443` 09cf7f1 - complete (mandb): fix an encoding prpblem of utf8 manuals `#D1446` 7a4a480 +- util (`ble/util/msleep`): fix hang in Cygwin by swithing from `/dev/udp/0.0.0.0/80` to `/dev/zero` `#D1452` 0000000 - global:work around bash-4.2 bug of `declare -gA` (reported by 0xC0ncord) `#D1470` 8856a04 - global: fix declaration of associative arrays for `ble-reload` `#D1471` 3cae6e4 @@ -40,7 +41,7 @@ - main: include hostname in local runtime directory `#D1444` 6494836 - global: update the style of document comments ff4c4e7 -- util: add function `ble/string#quote-words` `#D1451` 0000000 +- util: add function `ble/string#quote-words` `#D1451` f03b87b # ble-0.4.0-devel2 diff --git a/note.txt b/note.txt index 8d444ebb..b232cf14 100644 --- a/note.txt +++ b/note.txt @@ -3841,6 +3841,141 @@ bash_tips 2021-01-28 + * 2021-01-26 util: Cygwin 上で ble/util/msleep がフリーズしてしまう [#D1452] + + | 何故だろうか。普通にユーザコマンドとして実行した場合には特に問題は発生して + | いない。サブシェルで実行しても問題は発生していない。内部 stty でサブシェル + | で実行すると問題が起こる? + | + | →どうも繰り返し実行すると発生する様である。 + | + | 以下を実行するとかなりの確率で固まる。 + | ( echo {1..1000} & builtin read -t 0.000100 v < /dev/udp/0.0.0.0/80 ) >/dev/null + + 以下によって通常の bash でも固まるという事が分かった。Cygwin 特有の振る舞い + である。Linux 上で試した限りでは問題は起こらない。 + + ( echo {0..1000} >/dev/null & builtin read -t 0.001 v < /dev/udp/127.0.0.1/80 ) + + 他の実装だと exec 9<(sleep) を起動して置くという物や、builtin sleep を使う + という物がある。 + + a builtin sleep + + builtin sleep はコンパイラが利用可能なときにしか使えないのでこれに依存し + たくない。飽くまで exec 9<(sleep) を使って実装して可能であれば builtin + sleep を使うという様にする形になる。 + + b 現在の /dev/udp/0.0.0.0/80 を弄って解決できないか + + うーん。不思議だ。'echo {1..1000}' を a.sh に書き出して置いて以下の様にす + ると再現しない。 + + ( . a.sh >/dev/null & builtin read -t 0.001 v < /dev/udp/127.0.0.1/80 ) + + 関数を function a { echo {1..1000}; } として a>/dev/null とした場合は再現する。 + + * builtin read は read に置き換えても発生する。 + * 0.0.0.0 を 0.0.0.1 にすると通信エラーになって別の意味で使えない。 + * 127.0.0.1/80 でも再現する。 + * >/dev/null を >a.txt にしても再現する。 + * read を試みる前に様々なリダイレクトをしてみても状況は変わらない。 + * {0..1000} の部分や 0.001 の部分を変えると発生確率が下がる。 + + うーん。微妙。というか環境が Cygwin だけというのであれば、最初からバイナ + リを用意しておくという手もある。 + + うーん。やはり Cygwin ではもっと別の実装を考えた方が良いだろうか。 + + c exec 9< <(sleep) + + 改めて exec 9< <(sleep) を試してみた所、遅延は殆どない様なので、これを採 + 用する事にする。 + + 思えば今までにも時々あった Cygwin で固まってしまう問題はこれが原因だったの + かもしれない。直前に fork してから is-stdin-ready を確認する機会が余りなかっ + たり、或いはその他の条件で発生しにくかったりして再現しにくかったという事の + 気がする。という事を考えるとやはり /dev/udp/0.0.0.0/80 は今後は使わない方が + 良い気がする。 + + →c の方法を使う事にした。古いコミットを参考にしてコードを復元する。 + 8bb54be acb7163 d14557c f53c26d + + また udp によるコードを使いたくなるかもしれないので、取り敢えず今の所は + bleopt internal_msleep_socket というオプションで udp 方式に切り替えられる様 + にしてコードを残しておく事にする。 + + ---------------------------------------- + + procsubst による実装に切り替えてもやはり同様の問題が発生する様だ…。 + うーん。どうした物だろうか。というかこれは bug-bash に報告しても良いのではないか。 + 然し、normal Bash で再現させようとしても再現しない。然し症状としては同じなので、 + Cygwin における read のタイムアウトに問題があるという事は確かなのだろう。 + + うーん。やはり Cygwin 用に特別にコンパイル済み sleep builtin を提供する? + + 今試したら fifo が Cygwin 上でも動く様になっている。最近動く様になったのだ + ろうか。或いは cygwin バージョンの問題だろうか。うーん。取り敢えず試しに動 + かしてみて、それで失敗したら procsubst に切り替えるという作戦にする。 + + →駄目。やはり同じ問題が発生する。FIFO でも駄目という事。 + read -t を使うのが本質的に駄目という事なのだろう。 + 唯、確率は格段に小さくなっている。 + + sleep 10 | { echo {1..1000} >/dev/null & read -t 0.001 v; echo end; } + + この様にしている時には特に問題も発生しない様だ。 + + builtin read -t "$v" v < "$$.pipe" + + この実装にしても固まる時には固まる。 + + % 何と、builtin sleep を使っても同様に固まるという事が判明した。 + % つまり、read -t の問題ではない。Cygwin 自体に問題があるという事? + % スレッドが停止するともう二度と動かないという種類の何か…。 + % →と思ったら勘違いだった。builtin sleep を使っているつもりが、 + % 別の方式をつかっていたのだった。 + + 一応 /dev/zero は期待通りに動く。但し、CPU 100% になるという事には注意する。 + 短時間の sleep であれば /dev/zero に頼っても良いかもしれない。と考えたが、 + 短時間の sleep を繰り返し使う場合などを考えるとやはり cpu100% になるのは好 + ましくない気もする。 + + /dev/ptmx を試してみた。これはちゃんとブロックするし、勝手に停止してしまう + 事もないが代わりの問題として bash が終了しなくなってしまうという物がある。 + 然し、通常の bash で同様の事をしても特に問題は発生しない様だ。何故だろうか。 + というか exec 9<&- を実行しようとしただけで固まってしまう。これは問題である。 + + ble/util/msleep/.use-read-timeout socket check + ble/util/msleep/.use-read-timeout fifo.exec2 check || + ble/util/msleep/.use-read-timeout procsub + ble/util/msleep/.use-read-timeout fifo.open1 + ble/util/msleep/.use-read-timeout zero.open1 + ble/util/msleep/.use-read-timeout zero.exec1 + + -------------------------------------------------------------------------- + + 結局 loadable builtin を使う事にしようと思って実装したが…。loadable + builtin のライセンスはどうなっているのだったか。普通に考えるとこれは GPLv2 + に感染する気がする。という事は loadable builtin のソースコードをつけて配布 + するのは難しいという事になる。うーん。loadable builtin ならば OK という訳 + は…ないだろう。GPLv2 的に。調べたら正にそういう項目について記述されていた。 + + https://www.gnu.org/licenses/gpl-faq.ja.html#GPLAndPlugins + + 従って loadable builtins を使う方針は採用できない。結局、他の手法について考 + える必要があるのである。或いは確率が小さければ cygwin でも read -t を使って + 大丈夫だろうか、と思ったが conditional-sync を使っている限り、従来よりも格 + 段に問題が起こる確率が高い。やはり read -t は諦めるべきだろうか。 + + 或いは conditional-sync の時だけ別の方法を用いるという可能性もある…。が、 + 別の方法に心当たりがある訳ではない。どうしようもない。バイナリを添付する訳 + にも行かない。 + + -------------------------------------------------------------------------- + + 取り敢えず /dev/zero では未だ hang が起きた事はないので、これで様子見する事にする。 + * util,syntax,complete: 配列内容の記録時に @Q を使った print に切り替える [#D1451] 大した高速化ではないと思うがコードの整理も兼ねて。 diff --git a/src/def.sh b/src/def.sh index 1dd1fb7b..785cada3 100644 --- a/src/def.sh +++ b/src/def.sh @@ -11,6 +11,7 @@ function blehook/declare { blehook/declare EXIT blehook/declare INT blehook/declare ERR +blehook/declare unload # util.sh diff --git a/src/util.sh b/src/util.sh index f8ad4412..e3219c24 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2341,7 +2341,8 @@ function ble/util/msleep/.calibrate-loop { local ret nsec _ble_measure_count=1 v=0 _ble_util_msleep_delay=0 ble-measure 'ble/util/msleep 1' local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count - ((_ble_util_msleep_delay=(count*_ble_util_msleep_delay+delay)/(count+1))) + ((count<=0||delay<_ble_util_msleep_delay)) && _ble_util_msleep_delay=$delay # 最小値 + # ((_ble_util_msleep_delay=(count*_ble_util_msleep_delay+delay)/(count+1))) # 平均値 } function ble/util/msleep/calibrate { ble/util/msleep/.calibrate-loop &>/dev/null @@ -2349,6 +2350,147 @@ function ble/util/msleep/calibrate { ble/util/idle.continue } +## @fn ble/util/msleep/.use-read-timeout type +## @param[in] type +## FILE.OPEN +## FILE=fifo mkfifo によりファイルを作成します。 +## FILE=zero /dev/zero を開きます。 +## FILE=ptmx /dev/ptmx を開きます。 +## OPEN=open 毎回ファイルを開きます。 +## OPEN=exec1 ファイルを読み取り専用で開きます。 +## OPEN=exec2 ファイルを読み書き両用で開きます。 +## socket +## /dev/udp/0.0.0.0/80 を使います。 +## procsub +## 9< <(sleep) を使います。 +function ble/util/msleep/.use-read-timeout { + local msleep_type=$1 + _ble_util_msleep_fd= + case $msleep_type in + (socket) + _ble_util_msleep_delay1=10000 # short msleep にかかる時間 [usec] + _ble_util_msleep_delay2=50000 # /bin/sleep 0 にかかる時間 [usec] + function ble/util/msleep/.core2 { + ((v-=_ble_util_msleep_delay2)) + ble/bin/sleep $((v/1000000)) + ((v%=1000000)) + } + function ble/util/msleep { + local v=$((1000*$1-_ble_util_msleep_delay1)) + ((v<=0)) && v=100 + ((v>1000000+_ble_util_msleep_delay2)) && + ble/util/msleep/.core2 + ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) + ! builtin read -t "$v" v < /dev/udp/0.0.0.0/80 + } + function ble/util/msleep/.calibrate-loop { + local _ble_measure_threshold=10000 + local ret nsec _ble_measure_count=1 v=0 + + _ble_util_msleep_delay1=0 ble-measure 'ble/util/msleep 1' + local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count + ((count<=0||delay<_ble_util_msleep_delay1)) && _ble_util_msleep_delay1=$delay # 最小値 + + _ble_util_msleep_delay2=0 ble-measure 'ble/util/msleep/.core2' + local delay=$((nsec/1000)) + ((count<=0||delay<_ble_util_msleep_delay2)) && _ble_util_msleep_delay2=$delay # 最小値 + } ;; + (procsub) + _ble_util_msleep_delay=300 + ble/fd#alloc _ble_util_msleep_fd '< <( + [[ $- == *i* ]] && builtin trap -- '' INT QUIT + while kill -0 $$; do command sleep 300; done &>/dev/null + )' + function ble/util/msleep { + local v=$((1000*$1-_ble_util_msleep_delay)) + ((v<=0)) && v=100 + ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) + ! builtin read -t "$v" -u "$_ble_util_msleep_fd" v + } ;; + (*.*) + if local rex='^(fifo|zero|ptmx)\.(open|exec)([12])(-[a-z]+)?$'; [[ $msleep_type =~ $rex ]]; then + + # tmpfile + case ${BASH_REMATCH[1]} in + (fifo) + _ble_util_msleep_tmp=$_ble_base_run/$$.ble_util_msleep.pipe + if [[ ! -p $_ble_util_msleep_tmp ]]; then + [[ -e $_ble_util_msleep_tmp ]] && ble/bin/rm -rf "$_ble_util_msleep_tmp" + ble/bin/mkfifo "$_ble_util_msleep_tmp" + fi ;; + (zero) + _ble_util_msleep_tmp=/dev/zero ;; + (ptmx) + _ble_util_msleep_tmp=/dev/ptmx ;; + esac + + # redirection type + local redir='<' + ((BASH_REMATCH[3]==2)) && redir='<>' + + # open type + if [[ ${BASH_REMATCH[2]} == exec ]]; then + ble/fd#alloc _ble_util_msleep_fd "$redir \"\$_ble_util_msleep_tmp\"" + _ble_util_msleep_read='! builtin read -t "$v" -u "$_ble_util_msleep_fd" v' + else + _ble_util_msleep_read='! builtin read -t "$v" v '$redir' "$_ble_util_msleep_tmp"' + fi + + # fallback/switch + if [[ ${BASH_REMATCH[4]} == '-coreutil' ]]; then + _ble_util_msleep_switch=200 # [msec] + _ble_util_msleep_delay1=2000 # short msleep にかかる時間 [usec] + _ble_util_msleep_delay2=50000 # /bin/sleep 0 にかかる時間 [usec] + function ble/util/msleep { + if (($1<_ble_util_msleep_switch)); then + local v=$((1000*$1-_ble_util_msleep_delay1)) + ((v<=0)) && v=100 + ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) + builtin eval -- "$_ble_util_msleep_read" + else + local v=$((1000*$1-_ble_util_msleep_delay2)) + ((v<=0)) && v=100 + ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) + ble/bin/sleep "$v" + fi + } + function ble/util/msleep/.calibrate-loop { + local _ble_measure_threshold=10000 + local ret nsec _ble_measure_count=1 + + _ble_util_msleep_switch=200 + _ble_util_msleep_delay1=0 ble-measure 'ble/util/msleep 1' + local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count + ((count<=0||delay<_ble_util_msleep_delay1)) && _ble_util_msleep_delay1=$delay # 最小値を選択 + + _ble_util_msleep_delay2=0 ble-measure 'ble/bin/sleep 0' + local delay=$((nsec/1000)) + ((count<=0||delay<_ble_util_msleep_delay2)) && _ble_util_msleep_delay2=$delay # 最小値を選択 + ((_ble_util_msleep_switch=_ble_util_msleep_delay2/1000+10)) + } + else + function ble/util/msleep { + local v=$((1000*$1-_ble_util_msleep_delay)) + ((v<=0)) && v=100 + ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) + builtin eval -- "$_ble_util_msleep_read" + } + fi + fi ;; + esac + + # Note: 古い Cygwin では双方向パイプで "Communication error on send" というエラーになる。 + # 期待通りの振る舞いをしなかったらプロセス置換に置き換える。 #D1449 + if [[ :$opts: == *:check:* && $_ble_util_msleep_fd ]]; then + if builtin read -t 0.000001 -u "$_ble_util_msleep_fd" _ble_util_msleep_dummy 2>/dev/null; (($?!=142)); then + ble/fd#close _ble_util_msleep_fd + _ble_util_msleep_fd= + return 1 + fi + fi + return 0 +} + if ((_ble_bash>=40400)) && ble/util/msleep/.check-builtin-sleep; then _ble_util_msleep_builtin_available=1 _ble_util_msleep_delay=300 @@ -2456,49 +2598,16 @@ if ((_ble_bash>=40400)) && ble/util/msleep/.check-builtin-sleep; then function sleep { ble/builtin/sleep "$@"; } elif ((_ble_bash>=40000)) && [[ $OSTYPE != haiku* && $OSTYPE != minix* ]]; then if [[ $OSTYPE == cygwin* || $OSTYPE == msys* ]]; then - _ble_util_msleep_delay1=10000 # short msleep にかかる時間 [usec] - _ble_util_msleep_delay2=50000 # /bin/sleep 0 にかかる時間 [usec] - function ble/util/msleep/.core2 { - ((v-=_ble_util_msleep_delay2)) - ble/bin/sleep $((v/1000000)) - ((v%=1000000)) - } - function ble/util/msleep { - local v=$((1000*$1-_ble_util_msleep_delay1)) - ((v<=0)) && v=100 - ((v>1000000+_ble_util_msleep_delay2)) && - ble/util/msleep/.core2 - ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) - ! builtin read -t "$v" v < /dev/udp/0.0.0.0/80 - } - function ble/util/msleep/.calibrate-loop { - local _ble_measure_threshold=10000 - local ret nsec _ble_measure_count=1 v=0 - - _ble_util_msleep_delay1=0 ble-measure 'ble/util/msleep 1' - local delay=$((nsec/1000-1000)) count=$_ble_util_msleep_calibrate_count - ((_ble_util_msleep_delay1=(count*_ble_util_msleep_delay1+delay)/(count+1))) - - _ble_util_msleep_delay2=0 ble-measure 'ble/util/msleep/.core2' - local delay=$((nsec/1000)) - ((_ble_util_msleep_delay2=(count*_ble_util_msleep_delay2+delay)/(count+1))) - } + # Note: #D1452 socket (/dev/udp) で Cygwin が hang する。他の実装も全般に + # read -t は Cygwin では固まる可能性がある様だ。但し、発生する頻度は方法に + # よって異なる。仕方が無いので自前で loadable builtin をコンパイルする事に + # した。と思ったがライセンスの問題でこれを有効にする訳には行かない。 + [[ -f $_ble_base/lib/init-msleep.sh ]] && + source "$_ble_base/lib/init-msleep.sh" && + ble/util/msleep/.use-compiled-builtin || + ble/util/msleep/.use-read-timeout zero.exec1-coreutil else - _ble_util_msleep_delay=300 - _ble_util_msleep_fd= - _ble_util_msleep_tmp=$_ble_base_run/$$.ble_util_msleep.pipe - if [[ ! -p $_ble_util_msleep_tmp ]]; then - [[ -e $_ble_util_msleep_tmp ]] && ble/bin/rm -rf "$_ble_util_msleep_tmp" - ble/bin/mkfifo "$_ble_util_msleep_tmp" - fi - ble/fd#alloc _ble_util_msleep_fd "<> $_ble_util_msleep_tmp" - - function ble/util/msleep { - local v=$((1000*$1-_ble_util_msleep_delay)) - ((v<=0)) && v=100 - ble/util/sprintf v '%d.%06d' $((v/1000000)) $((v%1000000)) - ! builtin read -u "$_ble_util_msleep_fd" -t "$v" v - } + ble/util/msleep/.use-read-timeout fifo.exec2 fi elif ble/bin/.freeze-utility-path sleepenh; then function ble/util/msleep/.core { ble/bin/sleepenh "$1" &>/dev/null; }