Different behavoir when writing to stdout with 2 raw_fd_ostreams with or w/o redirection

Dear all,

Consider there is a program that writes to stdout using two different raw_fd_ostream-s:

#include “llvm/LLVMContext.h”
#include “llvm/Module.h”
#include “llvm/Support/raw_ostream.h”

using namespace llvm;

int main()
{
raw_fd_ostream S(STDOUT_FILENO, false);

outs() << “Hello”;
S << “, world!”;

return 0;
}

With this layout everything is fine, it prints “, world!Hello”

Now, make S definition global:

#include “llvm/LLVMContext.h”
#include “llvm/Module.h”
#include “llvm/Support/raw_ostream.h”

using namespace llvm;

raw_fd_ostream S(STDOUT_FILENO, false);

int main()
{
outs() << “Hello”;
S << “, world!”;

return 0;
}

And… surprisingly:

$ ./outs
Hello, world!$ ./outs &>result
$ cat result
HelloLLVM ERROR: IO failure on output stream.

So, no error with screen output and error when redirected to file. Why so?

Thanks,

  • D.

My guess is that in the second case the timing of the global
destructor of S is messing things up. Just a guess though.

-- Sean Silva

Dear all,

Consider there is a program that writes to stdout using two different raw_fd_ostream-s:

raw_fd_ostream does buffered I/O, so it’s not really meant to be used like this.

#include “llvm/LLVMContext.h”
#include “llvm/Module.h”
#include “llvm/Support/raw_ostream.h”

using namespace llvm;

int main()
{
raw_fd_ostream S(STDOUT_FILENO, false);

outs() << “Hello”;
S << “, world!”;

return 0;
}

With this layout everything is fine, it prints “, world!Hello”

Yes. raw_ostream does buffering. The output you see is a classic symptom of having two different buffers for the same file descriptor. You can make this work by manually flushing when switching from one buffer to another, if you’re careful.

Now, make S definition global:

#include “llvm/LLVMContext.h”
#include “llvm/Module.h”
#include “llvm/Support/raw_ostream.h”

using namespace llvm;

raw_fd_ostream S(STDOUT_FILENO, false);

int main()
{
outs() << “Hello”;
S << “, world!”;

return 0;
}

And… surprisingly:

$ ./outs
Hello, world!$ ./outs &>result
$ cat result
HelloLLVM ERROR: IO failure on output stream.

So, no error with screen output and error when redirected to file. Why so?

The difference in output is due to the difference in when the buffers happen to get destructed, because they flush their buffers in their destructors.

The error comes from the fact that outs() closes STDOUT_FILENO when the program exits. Your other raw_fd_ostream is getting unlucky and having its destructor run after the outs() object is destructed, so it ends up trying to write to a closed file descriptor. There’s no easy way to avoid this problem, as global destructor ordering is inconvenient to control. In general, it’s best to try to avoid this situation altogether.

Dan

Hi Dan, Sean,

Thanks for replies,

So does it mean user is not expected to use any other stream for stdout, but outs()? Although there is a comment “If you don’t want this behavior, don’t use outs().”, outs() is a static instance which always exists and creates problems, even if not used.

  • D.

2012/11/29 Dan Gohman <dan433584@gmail.com>

If you don’t call outs(), its static object isn’t constructed (or destructed).

Dan

Right, thank you!

2012/11/29 Dan Gohman <dan433584@gmail.com>