# HG changeset patch # User Bram Moolenaar # Date 1548368105 -3600 # Node ID bfbdef46aa7d6418c26a6409e96d288482879204 # Parent 327925217fe30a39249529c18b5a076a6d3e2c3d patch 8.1.0818: MS-Windows: cannot send large data with ch_sendraw() commit https://github.com/vim/vim/commit/240583869ae477202494dd01ef1e8e2bac650f10 Author: Bram Moolenaar Date: Thu Jan 24 23:11:49 2019 +0100 patch 8.1.0818: MS-Windows: cannot send large data with ch_sendraw() Problem: MS-Windows: cannot send large data with ch_sendraw(). Solution: Split write into several WriteFile() calls. (Yasuhiro Matsumoto, closes #3823) diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -80,24 +80,34 @@ fd_read(sock_T fd, char *buf, size_t len static int fd_write(sock_T fd, char *buf, size_t len) { + size_t todo = len; HANDLE h = (HANDLE)fd; - DWORD nwrite; + DWORD nwrite, size, done = 0; OVERLAPPED ov; - // If the pipe overflows while the job does not read the data, WriteFile - // will block forever. This abandons the write. - memset(&ov, 0, sizeof(ov)); - if (!WriteFile(h, buf, (DWORD)len, &nwrite, &ov)) + while (todo > 0) { - DWORD err = GetLastError(); - - if (err != ERROR_IO_PENDING) - return -1; - if (!GetOverlappedResult(h, &ov, &nwrite, FALSE)) - return -1; - FlushFileBuffers(h); + if (todo > MAX_NAMED_PIPE_SIZE) + size = MAX_NAMED_PIPE_SIZE; + else + size = todo; + // If the pipe overflows while the job does not read the data, WriteFile + // will block forever. This abandons the write. + memset(&ov, 0, sizeof(ov)); + if (!WriteFile(h, buf + done, size, &nwrite, &ov)) + { + DWORD err = GetLastError(); + + if (err != ERROR_IO_PENDING) + return -1; + if (!GetOverlappedResult(h, &ov, &nwrite, FALSE)) + return -1; + FlushFileBuffers(h); + } + todo -= nwrite; + done += nwrite; } - return (int)nwrite; + return (int)done; } static void diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -5369,7 +5369,7 @@ create_pipe_pair(HANDLE handles[2]) name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_NOWAIT, - 1, 65535, 0, 0, NULL); + 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL); if (handles[1] == INVALID_HANDLE_VALUE) return FALSE; diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -1980,3 +1980,21 @@ func Test_job_start_in_timer() unlet! g:val unlet! g:job endfunc + +func Test_raw_large_data() + try + let g:out = '' + let job = job_start(s:python . " test_channel_pipe.py", + \ {'mode': 'raw', 'drop': 'never', 'noblock': 1, + \ 'callback': {ch, msg -> execute('let g:out .= msg')}}) + + let want = repeat('X', 79999) . "\n" + call ch_sendraw(job, want) + let g:Ch_job = job + call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))}) + call assert_equal(want, substitute(g:out, '\r', '', 'g')) + finally + call job_stop(job) + unlet g:out + endtry +endfunc diff --git a/src/testdir/test_channel_pipe.py b/src/testdir/test_channel_pipe.py --- a/src/testdir/test_channel_pipe.py +++ b/src/testdir/test_channel_pipe.py @@ -56,4 +56,8 @@ if __name__ == "__main__": if typed.startswith("doubleerr "): print(typed[10:-1] + "\nAND " + typed[10:-1], file=sys.stderr) sys.stderr.flush() + if typed.startswith("XXX"): + print(typed, end='') + sys.stderr.flush() + break diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -788,6 +788,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 818, +/**/ 817, /**/ 816, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2467,6 +2467,10 @@ typedef enum { # define MAX_OPEN_CHANNELS 0 #endif +#if defined(WIN32) +# define MAX_NAMED_PIPE_SIZE 65535 +#endif + /* Options for json_encode() and json_decode. */ #define JSON_JS 1 /* use JS instead of JSON */ #define JSON_NO_NONE 2 /* v:none item not allowed */