EPIPE error #978

Closed
splhack opened this Issue Nov 10, 2016 · 11 comments

Projects

None yet

4 participants

@splhack
Member
splhack commented Nov 10, 2016

質問・報告の内容

jobにin_bufでbufferをwriteするときに、can_write_buf_lineがokにも関わらず(selectでfile descriptorがwritable)、writeで-1、errno=EPIPEになります。1回だけエラーが出たあと、そのあとは普通にwriteできるようです。macOSのpipeに対するなんらかのおまじないが足りないのではないかという予想ですが、よくわかりません。

macvim-dev/macvim#402

報告はMacVimですが、Vimでも発生しています。

Vimのバージョン

8.0.52 (たぶん8.0以降全部)

OSの種類/ディストリ/バージョン

macOS 10.11

使用している or 関係していそうなプラグイン

報告は ale + jscs w0rp/ale#39

jshintでは発生していません。

その他

@mattn
Member
mattn commented Nov 11, 2016

fixStdin ってのでバッファをすり替えてる感じに見えますが、これ関係しますかね。

https://github.com/jscs-dev/node-jscs/blob/44f9b86eb0757fd4ca05a81a50450c5f1b25c37b/lib/checker.js#L219

@mattn
Member
mattn commented Nov 11, 2016

als から拝借して再現スクリプト書いてみたんですが、Windows だと再現しませんね。

function! s:HandleExitVim(...)
    "echo a:000
endfunction

function! s:GatherOutputVim(...)
    echo a:000
endfunction

let s:job_options = {
   \   'in_mode': 'nl',
   \   'out_mode': 'nl',
   \   'err_mode': 'nl',
   \   'close_cb': function('s:HandleExitVim'),
   \}
let s:job_options.err_cb = function('s:GatherOutputVim')
let s:job_options.out_cb = function('s:GatherOutputVim')

if has('win32')
  let s:job = job_start('cmd /c jscs', s:job_options)
else
  let s:job = job_start('jscs', s:job_options)
endif
let s:ch = job_getchannel(s:job)
call ch_sendraw(s:ch, "{'foo': 'bar'}")
call ch_close_in(s:ch)
@splhack
Member
splhack commented Nov 11, 2016

ありがとうございます! それを元にして再現するスクリプトができました。

~/.jscsrcがあると再現しません → jscsがメッセージを出力しない

よーするに、job側がwriteすると、Vim側のwriteでEPIPE受けるってことですね。full duplexじゃないってことでしょうか?

@splhack
Member
splhack commented Nov 11, 2016
fcntl(fd_in[0], F_SETNOSIGPIPE, 1);

も試してみましたが、呼び出し方が悪いのか変化なし

@splhack
Member
splhack commented Nov 11, 2016

こーいうjobにwriteするだけでEPIPEですね

Thread.new do
  while true
    sleep 1
    puts "hoge"
  end
end

while true
  gets
end
@splhack
Member
splhack commented Nov 11, 2016 edited

macOSはpipeがhalf duplexなので、job側がwriteすると、VimのwriteでEPIPEになるので、そういうコマンドは使わないか、errnoがEPIPEだったら、read後にwriteしなおすような仕組みが必要な気がしてきました。

@ichizok
Member
ichizok commented Nov 11, 2016

単にwriteが終わる前にjscs側のstdinが閉じているということはないですか?
私は ↑ のrubyのスクリプトでは再現できませんでした (Vim 8.0.55)

@splhack
Member
splhack commented Nov 11, 2016

こういうひどいhackで、そのrubyスクリプトのEPIPEは回避できましたが

diff --git a/src/channel.c b/src/channel.c
index 16156ce..0266104 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -3477,12 +3477,17 @@ channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, char *fun
    did_log_msg = TRUE;
     }

+retry:
     if (part == PART_SOCK)
    res = sock_write(fd, (char *)buf, len);
     else
    res = fd_write(fd, (char *)buf, len);
     if (res != len)
     {
+   if (errno == EPIPE) {
+       channel_read(channel, part, "channel_send");
+       goto retry;
+   }
    if (!channel->ch_error && fun != NULL)
    {
        ch_errors(channel, "%s(): write failed", fun);
@splhack
Member
splhack commented Nov 11, 2016

@ichizok なんか単にそれだけな気がしてきました...

@ichizok
Member
ichizok commented Nov 11, 2016

channel のログを見ると、foo.js の内容を(1行ずつ)write している最中に No configuration ~~ のエラーメッセージを read して、以降 write error になっています。
試しに jscs/lib/cli.js を変更して、.jscsrc がある場合でも No configuration ~~ を出すようにして(かつ処理を継続させて)みましたが、エラーは出ませんでした。

diff --git a/lib/cli.js b/lib/cli.js
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -52,17 +52,17 @@ module.exports = function cli(program) {
      * Trying to load config.
      * Custom config path can be specified using '-c' option.
      */
-    if (!config && !program.preset && !program.autoConfigure) {
-        if (program.config) {
-            console.error('Configuration source', program.config, 'was not found.');
-        } else {
+    // if (!config && !program.preset && !program.autoConfigure) {
+    //     if (program.config) {
+    //         console.error('Configuration source', program.config, 'was not found.');
+    //     } else {
             console.error('No configuration found. Add a .jscsrc file to your project root or use the -c option.');
-        }
-
-        defer.reject(4);
-
-        return returnArgs;
-    }
+    //     }
+    //
+    //     defer.reject(4);
+    //
+    //     return returnArgs;
+    // }

     if (!args.length && process.stdin.isTTY && typeof program.autoConfigure !== 'string') {
         console.error('No input files specified. Try option --help for usage information.');```
@splhack
Member
splhack commented Nov 11, 2016

EPIPEのあと、復帰できたパターンを見つけた気がしたのですが、たぶん眠かっただけのようです。job側のstdinが閉じてるだけですねやっぱ。

@splhack splhack closed this Nov 11, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment