@@ -52,6 +52,17 @@ int _Py_open_cloexec_works = -1;
52
52
// The value must be the same in unicodeobject.c.
53
53
#define MAX_UNICODE 0x10ffff
54
54
55
+ /* Limit write size on terminals in Windows to keep the interpreter
56
+ feeling responsive.
57
+
58
+ This is higher than WRITE_LIMIT_CONSOLE because `.write()`
59
+ is targeted at non-console I/O (but may happen to touch a tty). Use
60
+ WinConsoleIO for best console interactivity.
61
+
62
+ This should ideally be bigger than DEFAULT_BUFFER_SIZE so common
63
+ case write to file on disk is quick. */
64
+ #define WRITE_LIMIT_INTERACTIVE (5 * 1024 * 1024)
65
+
55
66
// mbstowcs() and mbrtowc() errors
56
67
static const size_t DECODE_ERROR = ((size_t )-1 );
57
68
static const size_t INCOMPLETE_CHARACTER = (size_t )-2 ;
@@ -1922,6 +1933,24 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
1922
1933
int async_err = 0 ;
1923
1934
1924
1935
_Py_BEGIN_SUPPRESS_IPH
1936
+ #ifdef MS_WINDOWS
1937
+ /* isatty is guarded because don't want it in common case of
1938
+ writing DEFAULT_BUFFER_SIZE to regular files (gh-121940). */
1939
+ if (count > WRITE_LIMIT_INTERACTIVE ) {
1940
+ if (gil_held ) {
1941
+ Py_BEGIN_ALLOW_THREADS
1942
+ if (isatty (fd )) {
1943
+ count = _Py_LimitConsoleWriteSize (buf , count , WRITE_LIMIT_INTERACTIVE );
1944
+ }
1945
+ Py_END_ALLOW_THREADS
1946
+ } else {
1947
+ if (isatty (fd )) {
1948
+ count = _Py_LimitConsoleWriteSize (buf , count , WRITE_LIMIT_INTERACTIVE );
1949
+ }
1950
+ }
1951
+ }
1952
+
1953
+ #endif
1925
1954
if (count > _PY_WRITE_MAX ) {
1926
1955
count = _PY_WRITE_MAX ;
1927
1956
}
@@ -3081,3 +3110,52 @@ _Py_IsValidFD(int fd)
3081
3110
return (fstat (fd , & st ) == 0 );
3082
3111
#endif
3083
3112
}
3113
+
3114
+ #ifdef MS_WINDOWS
3115
+ static size_t
3116
+ _find_last_utf8_boundary (const char * buf , size_t len )
3117
+ {
3118
+ /* This function never returns 0, returns the original len instead */
3119
+ DWORD count = 1 ;
3120
+ if (len == 0 || (buf [len - 1 ] & 0x80 ) == 0 ) {
3121
+ return len ;
3122
+ }
3123
+ for (;; count ++ ) {
3124
+ if (count > 3 || count >= len ) {
3125
+ return len ;
3126
+ }
3127
+ if ((buf [len - count ] & 0xc0 ) != 0x80 ) {
3128
+ return len - count ;
3129
+ }
3130
+ }
3131
+ }
3132
+
3133
+ /* Put a soft limit on the number of bytes to be written.
3134
+
3135
+ In older versions of Windows a hard limit was necessary because
3136
+ there was a hard limit to the number of bytes (bpo-11395), but that
3137
+ is not the case in Windows 8+.
3138
+
3139
+ For Windows 8+ the console host synchronizes I/O operations which
3140
+ means a Ctrl-C doesn't generate an interrupt until after the write
3141
+ is completed. That means large writes which take multiple seconds
3142
+ will reduce responsiveness to interrupts.
3143
+
3144
+ This does a "soft cap" (not exact number of utf-16 bytes, but close
3145
+ enough) to maintain responsiveness of consoles on
3146
+ Windows (gh-121940). */
3147
+ size_t _Py_LimitConsoleWriteSize (const void * buf , size_t requested_size ,
3148
+ size_t cap_size ) {
3149
+ if (requested_size <= cap_size ) {
3150
+ return requested_size ;
3151
+ }
3152
+
3153
+ /* Fix for github issues gh-110913 and gh-82052.
3154
+
3155
+ Splitting utf-8 can't be done at arbitrary byte boundaries
3156
+ because that results in broken utf-8 byte sequences being
3157
+ presented to the user. */
3158
+ return _find_last_utf8_boundary (buf , cap_size );
3159
+ }
3160
+
3161
+ #endif
0 commit comments