[libc-commits] [libc] [libc] Lock the output stream for the 'puts' call (PR #76513)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Thu Dec 28 08:35:16 PST 2023


https://github.com/jhuber6 created https://github.com/llvm/llvm-project/pull/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.


>From b529ddb2218b5289216b8f7a88b4e9baa5fc30b6 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Thu, 28 Dec 2023 10:32:33 -0600
Subject: [PATCH] [libc] Lock the output stream for the 'puts' call

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.
---
 libc/src/stdio/generic/puts.cpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libc/src/stdio/generic/puts.cpp b/libc/src/stdio/generic/puts.cpp
index d8d69332566cd0..052977e5620646 100644
--- a/libc/src/stdio/generic/puts.cpp
+++ b/libc/src/stdio/generic/puts.cpp
@@ -15,14 +15,28 @@
 
 namespace LIBC_NAMESPACE {
 
+// Simple helper to unlock the file once destroyed.
+struct ScopedLock {
+  ScopedLock(LIBC_NAMESPACE::File *stream) : stream(stream) { stream->lock(); }
+  ~ScopedLock() { stream->unlock(); }
+
+private:
+  LIBC_NAMESPACE::File *stream;
+};
+
 LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
   cpp::string_view str_view(str);
+
+  // We need to lock the stream to ensure the newline is always appended.
+  ScopedLock lock(LIBC_NAMESPACE::stdout);
+
   auto result = LIBC_NAMESPACE::stdout->write(str, str_view.size());
   if (result.has_error())
     libc_errno = result.error;
   size_t written = result.value;
   if (str_view.size() != written) {
     // The stream should be in an error state in this case.
+    LIBC_NAMESPACE::stdout->unlock();
     return EOF;
   }
   result = LIBC_NAMESPACE::stdout->write("\n", 1);



More information about the libc-commits mailing list