11

I tried to link an external C++ function with my Rust application. This function works but it does not execute in the same order as it is called from Rust code.

Why does this happen? Is this documented?

Here is a listing of the Rust application:

extern crate libc;
use libc::c_int;

#[link(name = "Project1", kind = "static")]
extern "C" {
    pub fn lib_fun(i: c_int) -> c_int;
}

fn main() {
    unsafe {
        lib_fun(2);
    }
    println!("from Rust: {}", 2);
}

"Project1" library looks like this:

#include <stdio.h>

extern "C" {
    int lib_fun(int t) {
        printf("from C++: %d\n", t);
        return t;
    }
}

Expected output:

from C++: 2
from Rust: 2

The real output is in the reverse order:

from Rust: 2
from C++: 2

Is the external function lib_func executed in another thread? Why?

Details:

  • Platform: Windows 7, x64,
  • Rust: 1.26.0 (nightly),
  • C++: Microsoft Visual Studio Community 2017 Preview 15.7.0 Preview 2.0
  • Terminal: IntelliJ IDEA's integrated terminal.
Matthieu M.
  • 251,718
  • 39
  • 369
  • 642
Michail
  • 718
  • 3
  • 10
  • 9
    Definitely not *threading* (Rust does not create threads behind your back), so I suspect that you are running afoul of buffering on stdout; which is strange since I thought that using `\n` in the format string would cause `printf` to flush immediately. – Matthieu M. Mar 25 '18 at 11:19
  • Have you debugged your code and stepped through it to confirm that `lib_fun` actually is executed after your `println` call? Otherwise, like Matthieu, I would also assume this is caused by some buffer issue, and not execution order. – Max Vollmer Mar 25 '18 at 11:54
  • @Michail because Rust and C++ would each have their own buffer. – mcarton Mar 25 '18 at 12:14
  • @MaxVollmer lib_fun is actually executed and returned before println call. What the buffer issue can happens? As mentioned by Matthieu, this is strange espesially since "\n" is using. – Michail Mar 25 '18 at 12:18
  • 1
    Try flushing `stdout` manually in C++. – Veedrac Mar 25 '18 at 12:19
  • @mcarton Aren't they use a common system buffer? Is this documented somewhere in rust docs? And is there a way to make a right output order? – Michail Mar 25 '18 at 12:22
  • 2
    @Veedrac Thanks. I add `fflush(stdout);` in the end of C++ function and things became happens in right order. – Michail Mar 25 '18 at 12:30
  • 1
    I can't reproduce this with rustc 1.24.1 and GCC 7.3.1 on Arch Linux x64. Which C++ compiler and which version of Rust are you using? – Francis Gagné Mar 25 '18 at 14:47
  • @Francis It happens in Windows x64, Rust nightly 1.26.0. I gess `printf("...\n")` flush buffer in Linux, or iC++ and Rust don't use different buffers in Linux, or so on. Problem is solved howbeit, thanks. – Michail Mar 25 '18 at 14:51
  • 1
    @Michail: I do not think that Rust is the problem here, I suspect your C++ implementation is. Which C++ compiler are you using? Visual Studio? (which version?) Or something else? – Matthieu M. Mar 25 '18 at 14:52
  • @Matthieu You are right. I read more about `printf` and clarify that it not guarantee flushing. I find something about this: https://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strinright – Michail Mar 25 '18 at 14:59
  • @Michail: Ah! I thought it was always guaranteed to flush on newline, didn't know that there was an exemption when streaming to a file. Where you streaming to file? Or could it be that your terminal is not properly detected? It's really fun corner case you managed to unearth! – Matthieu M. Mar 25 '18 at 15:03
  • 1
    @Michail: As mentioned, it's likely the C++ application which is not behaving as expected; so I was wondering which version of C++ compiler you were using. Normally, it should flush if it prints to a terminal... but maybe the C++ implementation you have has issues recognizing your IntelliJ terminal as an interactive device and thinks it's a file? (or maybe the C++ implementation is just non-comformant). – Matthieu M. Mar 25 '18 at 15:16

1 Answers1

6

External C++ code uses its own buffer to write to stdout and it is flushed to the system buffer later than the Rust caller does. printf("...\n") doesn't flush the stdout buffer as I expected.

Instead, I need to flush it manually, for example by calling fflush(stdout);

Thanks for this answer to @Veedrac

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
Michail
  • 718
  • 3
  • 10
  • 1
    This isn't universally applicable. On macOS 10.13.3 with Clang (`Apple LLVM version 9.0.0 (clang-900.0.39.2)`), the output is in the correct order. The order is also correct when using Visual Studio 2017 on Windows 10. This is up to the implementation details of the standard library of the platform — it *should* flush when there is a newline in the `printf`. – Shepmaster Mar 25 '18 at 14:57
  • 1
    _The documentation of printf doesn't mention flushing at all._ This post I found at https://stackoverflow.com/questions/49475166/why-do-a-rust-function-and-a-ffi-c-function-execute-in-reverse-order?noredirect=1#comment85956742_49475166 As I understand it MAY flush with `\n` but not SHOULD. May be it depends from OS and/or C++ compiler. – Michail Mar 25 '18 at 15:16
  • 4
    Flushing behavior often varies with `isatty(fileno(stdout))`. So the same OS, same code, same compiler, **same binary**, can have different buffering behavior depending on how the binary is invoked. – Ben Voigt Mar 25 '18 at 15:32