Skip to content

Commit 1bb85fa

Browse files
authored
[libc] Lock the output stream for the 'puts' call (#76513)
Summary: The `puts` function consists of an initial write and then another write to append the newline. When executing code in parallel, it is possible for these writes to becomes disjointed. This code adds an explicit lock call to ensure that the string is always appended by the newline as the users expects. Wasn't sure if this required a test as it would be difficult since reproducing it would be flaky.
1 parent fc9dbc9 commit 1bb85fa

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

libc/src/stdio/generic/puts.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,34 @@
1515

1616
namespace LIBC_NAMESPACE {
1717

18+
namespace {
19+
20+
// Simple helper to unlock the file once destroyed.
21+
struct ScopedLock {
22+
ScopedLock(LIBC_NAMESPACE::File *stream) : stream(stream) { stream->lock(); }
23+
~ScopedLock() { stream->unlock(); }
24+
25+
private:
26+
LIBC_NAMESPACE::File *stream;
27+
};
28+
29+
} // namespace
30+
1831
LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
1932
cpp::string_view str_view(str);
20-
auto result = LIBC_NAMESPACE::stdout->write(str, str_view.size());
33+
34+
// We need to lock the stream to ensure the newline is always appended.
35+
ScopedLock lock(LIBC_NAMESPACE::stdout);
36+
37+
auto result = LIBC_NAMESPACE::stdout->write_unlocked(str, str_view.size());
2138
if (result.has_error())
2239
libc_errno = result.error;
2340
size_t written = result.value;
2441
if (str_view.size() != written) {
2542
// The stream should be in an error state in this case.
2643
return EOF;
2744
}
28-
result = LIBC_NAMESPACE::stdout->write("\n", 1);
45+
result = LIBC_NAMESPACE::stdout->write_unlocked("\n", 1);
2946
if (result.has_error())
3047
libc_errno = result.error;
3148
written = result.value;

0 commit comments

Comments
 (0)