Skip to content

Commit a5e3975

Browse files
committed
Improve non-buffering strategy for FileStream
1 parent 86adef5 commit a5e3975

File tree

3 files changed

+9
-19
lines changed

3 files changed

+9
-19
lines changed

Src/IronPython.Modules/nt.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -883,8 +883,6 @@ public static void mkdir(CodeContext context, [NotNone] Bytes path, [ParamDictio
883883
public static void mkdir(CodeContext context, object? path, [ParamDictionary, NotNone] IDictionary<string, object> kwargs, [NotNone] params object[] args)
884884
=> mkdir(ConvertToFsString(context, path, nameof(path)), kwargs, args);
885885

886-
private const int DefaultBufferSize = 4096;
887-
888886
[Documentation("""
889887
open(path, flags, mode=511, *, dir_fd=None)
890888
@@ -940,15 +938,16 @@ public static object open(CodeContext/*!*/ context, [NotNone] string path, int f
940938
// open it again w/ just read access.
941939
fs = new FileStream(path, fileMode, FileAccess.Write, FileShare.None);
942940
fs.Close();
943-
s = fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, DefaultBufferSize, options);
941+
s = fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1, options);
944942
} else if (access == FileAccess.ReadWrite && fileMode == FileMode.Append) {
945943
// .NET doesn't allow Append w/ access != Write, so open the file w/ Write
946944
// and a secondary stream w/ Read, then seek to the end.
947-
s = fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, options);
948-
rs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, DefaultBufferSize, options);
945+
s = fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 1, options);
946+
rs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1, options);
949947
rs.Seek(0L, SeekOrigin.End);
950948
} else {
951-
s = fs = new FileStream(path, fileMode, access, FileShare.ReadWrite, DefaultBufferSize, options);
949+
// Use a 1 byte buffer size to disable buffering
950+
s = fs = new FileStream(path, fileMode, access, FileShare.ReadWrite, 1, options);
952951
}
953952
rs ??= s;
954953

Src/IronPython/Runtime/PythonFileManager.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,10 @@ internal sealed class StreamBox {
2929
private int _id = -1;
3030
private Stream _readStream;
3131
private Stream _writeStream;
32-
private readonly bool _writeNeedsFlush;
3332

3433
public StreamBox(Stream readStream, Stream writeStream) {
3534
_readStream = readStream;
3635
_writeStream = writeStream;
37-
38-
// a hack but it does improve perfomance a lot if flushing is not needed
39-
_writeNeedsFlush = writeStream switch {
40-
FileStream => true, // FileStream is buffered by default, used on Windows and mostly Mono
41-
PosixFileStream => false, // PosixFileStream is unbuffered, used on .NET/Posix
42-
UnixStream => false, // UnixStream is unbuffered, used sometimes on Mono
43-
_ => true // if in doubt, flush
44-
};
4536
}
4637

4738
public StreamBox(Stream stream) : this(stream, stream) { }
@@ -162,8 +153,8 @@ public int Write(IPythonBuffer buffer) {
162153
count = buffer.NumBytes();
163154
_writeStream.Write(bytes, 0, count);
164155
#endif
165-
if (_writeNeedsFlush) {
166-
_writeStream.Flush(); // IO at this level is not supposed to buffer so we need to call Flush.
156+
if (ClrModule.IsMono && count == 1) {
157+
_writeStream.Flush(); // IO at this level is not supposed to buffer so we need to call Flush (only needed on Mono)
167158
}
168159
if (!IsSingleStream) {
169160
_readStream.Seek(_writeStream.Position, SeekOrigin.Begin);

Tests/test_io_stdlib.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
## Run selected tests from test_io from StdLib
77
##
88

9-
from iptest import is_ironpython, is_mono, generate_suite, run_test
9+
from iptest import is_ironpython, is_mono, is_windows, generate_suite, run_test
1010

1111
import test.test_io
1212

@@ -100,7 +100,7 @@ def load_tests(loader, standard_tests, pattern):
100100
test.test_io.PyMiscIOTest('test_warn_on_dealloc_fd'), # AssertionError: ResourceWarning not triggered
101101
]
102102

103-
if is_mono:
103+
if is_mono or is_windows:
104104
failing_tests += [
105105
test.test_io.PyTextIOWrapperTest('test_seek_append_bom'), # OSError: [Errno -2146232800] Unable seek backward to overwrite data that previously existed in a file opened in Append mode.
106106
]

0 commit comments

Comments
 (0)