index | ~dongdigua

A Rust autoflusher that doesn't work with Rust

$Id: autoflusher_rs.org,v 25.4 2025/05/18 20:16:48 dongdigua Exp $

A while ago I read about Ted Unangst's autoflusher,

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

static void *
flusher(void *arg)
{
        while (1) {
                sleep(3);
                fflush(stdout);
        }
}

__attribute__((constructor))
void
herewego(void)
{
        pthread_t thread;

        pthread_create(&thread, NULL, flusher, NULL);
}

so I want to implement the same thing in Rust (or Crust?)

First I came up with this (expanded from ctor crate)

#![no_main]

use std::thread;
use std::time::Duration;
use std::io::Write;

#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
static FLUSHER: extern fn() = {
    extern fn force_flush() {
        thread::spawn(|| loop {
            std::io::stdout().flush().unwrap();
            thread::sleep(Duration::from_millis(1000));
        });
    }
    force_flush
};

and a C program for testing:

#include <stdio.h>
#include <unistd.h>

int
main() {
    printf("Hello World");
    for (;;)
        pause();
    return 0;
}

then

rustc --crate-type cdylib -o libflusher.so flusher.rs
LD_PRELOAD=./libflusher.so ./test

don't work

But the original C code works, so fflush(3) is ok, right?

--- a/flusher.rs
+++ b/flusher.rs
@@ -3,15 +3,24 @@
 use std::thread;
 use std::time::Duration;
 use std::io::Write;
+use std::ffi::{c_int, c_void};

 #[used]
 #[cfg_attr(target_os = "linux", link_section = ".init_array")]
 static FLUSHER: extern fn() = {
     extern fn force_flush() {
         thread::spawn(|| loop {
-            std::io::stdout().flush().unwrap();
+            unsafe {fflush(stdout)};
             thread::sleep(Duration::from_millis(1000));
         });
     }
     force_flush
 };
+
+#[repr(C)]
+struct FILE(c_void);
+
+extern "C" {
+    fn fflush(stream: *mut FILE) -> c_int;
+    static mut stdout: *mut FILE;
+}

Now the C program is working, then what about Rust?

use std::ffi::c_int;

fn main() {
    print!("Hello World");
    extern "C" {
        fn pause() -> c_int;
    }
    loop {unsafe {pause()};}
}

DON'T WORK!

WHY? Because Rust uses it's internal buffer, different from libc's.
So what if I use the first flusher (before diff).

DON'T WORK EITHER!! WHYYYYY?
I guess it's something about stdout()'s lock and .init_array.

dongdigua CC BY-NC-SA 禁止转载到私域(公众号,非自己托管的博客等)

Email me to add comment

Proudly made with Emacs Org mode

Date: 2025-05-17 Sat 00:00 Size: 8.4K (≈ 1.2637 mg CO2e)