Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-1127] print() should be less than fully buffered sometimes #43740

Open
gparker42 mannequin opened this issue Apr 1, 2016 · 17 comments
Open

[SR-1127] print() should be less than fully buffered sometimes #43740

gparker42 mannequin opened this issue Apr 1, 2016 · 17 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella

Comments

@gparker42
Copy link
Mannequin

gparker42 mannequin commented Apr 1, 2016

Previous ID SR-1127
Radar rdar://25302556
Original Reporter @gparker42
Type Bug
Additional Detail from JIRA
Votes 2
Component/s Standard Library
Labels Bug
Assignee None
Priority Medium

md5: 6ca15fad5ef935d4907965d6a9a7193d

Issue Description:

print() currently uses stdout's default buffering. It should be unbuffered or line-buffered in at least some sufficiently terminal-like conditions. This would improve debugging and command-line use.

func testExample() {
print("foo") // is not printed before the crash
assert(false)
}

@drewcrawford
Copy link
Contributor

Now that you mention it, this might be the mechanism behind SR-752.

@gribozavr
Copy link
Collaborator

We are using C stdio APIs to print to stdout. If you want to change how stdout buffering works, then your complaint is equally applicable to printf() and putchar(), so I think you should talk to your libc implementors instead.

@drewcrawford
Copy link
Contributor

As I mentioned on-list, IMHO this C-ism is outdated and harmful.

The hour is much too late to fix this in C, but we are immature enough that it would not break lots of things to fix it here.

@gribozavr
Copy link
Collaborator

Sorry, I'd like some more concrete arguments than 'outdated and harmful'.

@drewcrawford
Copy link
Contributor

'outdated and harmful' was a summary of this swift-dev thread.

But to duplicate the argument again here:

  • History aside, the behavior did surprise me, and surprised at least one radar reporter

  • I suspect most swift programs will need to opt out of default behavior, so is this really the right default?

  • Why should swift print follow C printf? Swift is not C, neither is print printf.

I understand a default position of "follow C unless there's a reason not to." But now we have reasons not to, so I think it is the "follow C" position that could use concrete argument.

Sorry for bikeshedding in the bugtracker; in my mind calling this a "bug" may be a bit premature, as the right behavior is not clearly defined and requires design. But it was filed and so here we are.

@gribozavr
Copy link
Collaborator

> History aside, the behavior did surprise me, and surprised at least one radar reporter

There are many things that surprise people...

> I suspect most swift programs will need to opt out of default behavior, so is this really the right default?

What is the usecase for print()? print() is not designed to be a reliable logging mechanism.

> Why should swift print follow C printf? Swift is not C, neither is print printf.

Because we want to interoperate with stdio buffering, make sure that mixing print() and printf() (which is a frequent use case in Swift, when calling C libraries) does not produce torn lines.

@drewcrawford
Copy link
Contributor

> There are many things that surprise people...

I guess you are suggesting that some solution would still be surprising. What I don't understand is why speculation about what might be surprising trumps received reports of actual surprise. Or is the present behavior supported by comparable reports?

> What is the usecase for print()? print() is not designed to be a reliable logging mechanism.

The usecase I have in mind is "printing progress messages". For example, here or here to use first-party examples. In practice, these messages cannot be read in a timely way by tooling (since print to pipes are fully buffered).

Is your view that SwiftPM incorrectly uses print here, and should move to something else instead?

> make sure that mixing print() and printf() (which is a frequent use case in Swift, when calling C libraries) does not produce torn lines.

This reads like an argument against no buffering, not an argument against line buffering.

@gribozavr
Copy link
Collaborator

> Or is the present behavior supported by comparable reports?

The present behavior is motivated by the requirement of stdio compatibility and previous reports that print() is unbuffered and slow.

> The usecase I have in mind is "printing progress messages".

If you want them to be machine-parseable, then you are implementing some kind of communication protocol, and you should be controlling the framing and segmentation in the stream yourself.

> In practice, these messages cannot be read in a timely way by tooling (since print to pipes are fully buffered).
> Is your view that SwiftPM incorrectly uses print here, and should move to something else instead?

I think the tooling is probably using SwiftPM incorrectly. Did SwiftPM ever promise that this output is machine-parseable, won't be localized etc? If you need machine-readable output from SwiftPM, it should be an explicit mode provided by it.

@drewcrawford
Copy link
Contributor

> Did SwiftPM ever promise that this output is machine-parseable?

Well it never promised its output was human-parseable, but in the interests of not veering off the road, let me try this. Let's stipulate some tooling (e.g., Xcode) that merely displays the live output to the user and does not interpret it in any way.

Is hypothetical-Xcode wrong to do that?

@gribozavr
Copy link
Collaborator

> Well it never promised its output was human-parseable

That's the default assumption for things printed to stdout.

> Let's stipulate some tooling (e.g., Xcode) that merely displays the live output to the user and does not interpret it in any way.
> Is hypothetical-Xcode wrong to do that?

No. But there would be no issues in that case, since the SwiftPM would be running in a PTY, and stdio would use the right defaults, which would be line buffered.

@drewcrawford
Copy link
Contributor

> But there would be no issues in that case, since the SwiftPM would be running in a PTY, and stdio would use the right defaults,

So in your view, the "real bug" is that any tooling that wants line buffering should use PTY?

For example, docker build (which is how I build a lot of Swift projects for Linux) does not (and will not) allocate a PTY. Your position is that they are wrong, and my recourse should be to fork Docker, "fix" their "bug", so I can see these prints?

I'm not sure you're wrong, I'm just following the trail to its logical conclusion.

@gribozavr
Copy link
Collaborator

I would say yes. They should already need to do it for fixing the same "bug" when the output is generated by printf() by any other piece of software. I don't see how changing Swift here would help docker build in the grand scheme of things where non-Swift software exists.

It also seems fair to me that if SwiftPM really wants to make sure that at some particular point the data is written into the file descriptor, it would call fflush() itself. This isn't different from any other file.

@swift-ci
Copy link
Collaborator

swift-ci commented Apr 2, 2016

Comment by Johannes Schriewer (JIRA)

I wonder what the use case of print is...

1. When i use it for debug output i would prefer it in the right order, speed is not important.
2. Logging is apparently not a valid use case
3. Outputting lots of text so speed matters... when?
4. Internal use in stdlib? For example printing stacktraces? I prefer them to be in order!

So it is valid for user interaction on a PTY only? Then this has to be in the documentation.

@swift-ci
Copy link
Collaborator

Comment by Jannis Gebauer (JIRA)

Python uses the same C API but allows to set the PYTHON_UNBUFFERED environment variable to override the default behaviour.

PYTHON_UNBUFFERED
Force the binary layer of the stdout and stderr streams (which is available as their buffer attribute) to be unbuffered. The text I/O layer will still be line-buffered if writing to the console, or block-buffered if redirected to a non-interactive file.

@swift-ci
Copy link
Collaborator

swift-ci commented Feb 1, 2022

Comment by Dave Gross (JIRA)

I realize this is an old ticket, but my team just ran into this "issue" as well and were very surprised when we figured out why our logs weren't showing up in some cases. Our use case is to use print() for logging combined with launchd's "StandardOutPath" parameter to turn it into file logging. Hence why we log with print(). We can get around it by adding the flush() statements, but just wanted to chime in that it was unexpected to have it behave this way.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@tristanlabelle
Copy link
Contributor

tristanlabelle commented Jun 1, 2023

Adding to the conversation here, this behavior is making it difficult to debug our code and failing tests (here at The Browser Company), as we don't reliably see the outputs of our prints as we step through the code, and might never see the output when hitting a crash. This a surprising behavior that we're not used to seeing with other programming languages, so we think of it as a bug. This is while using Swift on Windows.

@al45tair
Copy link
Contributor

al45tair commented Nov 6, 2023

FWIW, I think the actual problem in the original post is that Swift's assertion failure code doesn't flush stdio. C's abort() does, at least according to POSIX (ISO muddied the waters by suggesting that abort() might be async-signal-safe, apparently, which would preclude flushing for hopefully obvious reasons).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

5 participants