Jelajahi Sumber

Use BufWriter (instead of LineWriter) for FILEs other than stdout and stderr

BufWriter has more capacity (8k vs 1k) and doesn't flush the stream after '\n'.
That change helps to reduce the number of syscalls, especially when dealing with text files.

Since BufWriter has a different way of getting number of pending elements than LineWriter -
Pending trait was introduced to deal with that.
Mateusz Tabaka 4 tahun lalu
induk
melakukan
02f202ff83

+ 2 - 2
src/header/stdio/default.rs

@@ -2,13 +2,13 @@ use super::{constants, Buffer, BUFSIZ, FILE};
 use core::{cell::UnsafeCell, ptr};
 
 use crate::{fs::File, io::LineWriter, platform::types::*, sync::Mutex};
-use alloc::vec::Vec;
+use alloc::{boxed::Box, vec::Vec};
 
 pub struct GlobalFile(UnsafeCell<FILE>);
 impl GlobalFile {
     fn new(file: c_int, flags: c_int) -> Self {
         let file = File::new(file);
-        let writer = LineWriter::new(unsafe { file.get_ref() });
+        let writer = Box::new(LineWriter::new(unsafe { file.get_ref() }));
         GlobalFile(UnsafeCell::new(FILE {
             lock: Mutex::new(()),
 

+ 1 - 1
src/header/stdio/ext.rs

@@ -7,7 +7,7 @@ use crate::{
 pub extern "C" fn __fpending(stream: *mut FILE) -> size_t {
     let stream = unsafe { &mut *stream }.lock();
 
-    stream.writer.inner.buf.len() as size_t
+    stream.writer.pending()
 }
 
 #[no_mangle]

+ 2 - 2
src/header/stdio/helpers.rs

@@ -4,7 +4,7 @@ use super::{constants::*, Buffer, FILE};
 use crate::{
     fs::File,
     header::{errno, fcntl::*, string::strchr},
-    io::LineWriter,
+    io::BufWriter,
     platform::{self, types::*},
     sync::Mutex,
 };
@@ -62,7 +62,7 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> {
     }
 
     let file = File::new(fd);
-    let writer = LineWriter::new(file.get_ref());
+    let writer = Box::new(BufWriter::new(file.get_ref()));
 
     Some(Box::into_raw(Box::new(FILE {
         lock: Mutex::new(()),

+ 32 - 2
src/header/stdio/mod.rs

@@ -24,7 +24,7 @@ use crate::{
         string::{self, strlen},
         unistd,
     },
-    io::{self, BufRead, LineWriter, Read, Write},
+    io::{self, BufRead, BufWriter, LineWriter, Read, Write},
     platform::{self, errno, types::*, Pal, Sys, WriteByte},
     sync::Mutex,
 };
@@ -69,6 +69,27 @@ impl<'a> DerefMut for Buffer<'a> {
     }
 }
 
+pub trait Pending {
+    fn pending(&self) -> size_t;
+}
+
+impl<W: core_io::Write> Pending for BufWriter<W> {
+    fn pending(&self) -> size_t {
+        self.buf.len() as size_t
+    }
+}
+
+impl<W: core_io::Write> Pending for LineWriter<W> {
+    fn pending(&self) -> size_t {
+        self.inner.buf.len() as size_t
+    }
+}
+
+pub trait Writer: Write + Pending {}
+
+impl<W: core_io::Write> Writer for BufWriter<W> {}
+impl<W: core_io::Write> Writer for LineWriter<W> {}
+
 /// This struct gets exposed to the C API.
 pub struct FILE {
     lock: Mutex<()>,
@@ -81,7 +102,7 @@ pub struct FILE {
     read_size: usize,
     unget: Vec<u8>,
     // pub for stdio_ext
-    pub(crate) writer: LineWriter<File>,
+    pub(crate) writer: Box<dyn Writer + Send>,
 
     // Optional pid for use with popen/pclose
     pid: Option<c_int>,
@@ -1145,3 +1166,12 @@ pub unsafe extern "C" fn vsscanf(s: *const c_char, format: *const c_char, ap: va
     let reader = (s as *const u8).into();
     scanf::scanf(reader, format, ap)
 }
+
+pub unsafe fn flush_io_streams() {
+    let flush = |stream: *mut FILE| {
+        let stream = &mut *stream;
+        stream.flush()
+    };
+    flush(stdout);
+    flush(stderr);
+}

+ 3 - 0
src/header/stdlib/mod.rs

@@ -16,6 +16,7 @@ use crate::{
         errno::{self, *},
         fcntl::*,
         limits,
+        stdio::flush_io_streams,
         string::*,
         time::constants::CLOCK_MONOTONIC,
         unistd::{self, sysconf, _SC_PAGESIZE},
@@ -297,6 +298,8 @@ pub unsafe extern "C" fn exit(status: c_int) {
 
     pthread_terminate();
 
+    flush_io_streams();
+
     Sys::exit(status);
 }
 

+ 1 - 0
tests/expected/wchar/putwchar.stdout

@@ -0,0 +1 @@
+zß水🍌