[libc-commits] [libc] c88708e - [libc] Fix the values of TCOFLUSH and TCIOFLUSH (#236513)
via libc-commits
libc-commits at lists.llvm.org
Thu Oct 29 10:50:14 PDT 2026
Author: Mike Lothian
Date: 2026-10-29T10:50:09-07:00
New Revision: c88708ea8e617eef102e0e6390f88adcc6fe3a5f
URL: https://github.com/llvm/llvm-project/commit/c88708ea8e617eef102e0e6390f88adcc6fe3a5f
DIFF: https://github.com/llvm/llvm-project/commit/c88708ea8e617eef102e0e6390f88adcc6fe3a5f.diff
LOG: [libc] Fix the values of TCOFLUSH and TCIOFLUSH (#236513)
tcflush passes its argument straight to the TCFLSH ioctl, so the values
must be the kernel's, which are 0 for TCIFLUSH, 1 for TCOFLUSH and 2 for
TCIOFLUSH. The last two were swapped, so a caller asking to discard
unsent output also discarded the input it had not read yet
Add a test that leaves a line of input waiting on a pseudoterminal and
checks which of the three flushes drops it
AI disclosure: this patch was written with Claude Code. I reviewed the
change and ran the new test; it fails without the fix and passes with it
Assisted-by: Claude
Added:
libc/test/src/termios/tcflush_test.cpp
Modified:
libc/include/llvm-libc-macros/linux/termios-macros.h
libc/test/src/termios/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/include/llvm-libc-macros/linux/termios-macros.h b/libc/include/llvm-libc-macros/linux/termios-macros.h
index 668cfe27abaabf..4998f3dc3a32a2 100644
--- a/libc/include/llvm-libc-macros/linux/termios-macros.h
+++ b/libc/include/llvm-libc-macros/linux/termios-macros.h
@@ -1,10 +1,15 @@
-//===-- Definition of macros from termios.h -------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of macros from termios.h.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_MACROS_LINUX_TERMIOS_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_TERMIOS_MACROS_H
@@ -155,8 +160,8 @@
// Symbolic constants for use with tcflush function.
#define TCIFLUSH 0 // Flush pending input
-#define TCIOFLUSH 1 // Flush pending input and unstransmitted output
-#define TCOFLUSH 2 // Flush unstransmitted output
+#define TCOFLUSH 1 // Flush untransmitted output
+#define TCIOFLUSH 2 // Flush pending input and untransmitted output
// Symbolic constantf for use with tcflow function.
#define TCOOFF 0 // Transmit a STOP character, intended to suspend input data
diff --git a/libc/test/src/termios/CMakeLists.txt b/libc/test/src/termios/CMakeLists.txt
index b85203367b603d..30096fececc91b 100644
--- a/libc/test/src/termios/CMakeLists.txt
+++ b/libc/test/src/termios/CMakeLists.txt
@@ -24,6 +24,31 @@ add_libc_test(
libc.test.UnitTest.ErrnoSetterMatcher
)
+add_libc_test(
+ tcflush_test
+ SUITE
+ libc-termios-tests
+ SRCS
+ tcflush_test.cpp
+ DEPENDS
+ libc.hdr.fcntl_macros
+ libc.hdr.poll_macros
+ libc.hdr.sys_ioctl_macros
+ libc.hdr.termios_macros
+ libc.hdr.types.struct_pollfd
+ libc.src.__support.libc_errno
+ libc.src.errno.errno
+ libc.src.fcntl.open
+ libc.src.poll.poll
+ libc.src.stdio.snprintf
+ libc.src.sys.ioctl.ioctl
+ libc.src.termios.tcflush
+ libc.src.unistd.close
+ libc.src.unistd.write
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
add_libc_test(
tcgetwinsize_test
SUITE
diff --git a/libc/test/src/termios/tcflush_test.cpp b/libc/test/src/termios/tcflush_test.cpp
new file mode 100644
index 00000000000000..23b1c8f3a23bc7
--- /dev/null
+++ b/libc/test/src/termios/tcflush_test.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for tcflush.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/fcntl_macros.h"
+#include "hdr/poll_macros.h"
+#include "hdr/sys_ioctl_macros.h"
+#include "hdr/termios_macros.h"
+#include "hdr/types/struct_pollfd.h"
+#include "src/__support/libc_errno.h"
+#include "src/fcntl/open.h"
+#include "src/poll/poll.h"
+#include "src/stdio/snprintf.h"
+#include "src/sys/ioctl/ioctl.h"
+#include "src/termios/tcflush.h"
+#include "src/unistd/close.h"
+#include "src/unistd/write.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace {
+
+bool has_pending_input(int fd) {
+ int pending = 0;
+ LIBC_NAMESPACE::ioctl(fd, FIONREAD, &pending);
+ return pending > 0;
+}
+
+} // namespace
+
+// Each test gets a pseudoterminal with a line of input waiting on the
+// subsidiary side. The descriptors are closed in TearDown, so no path out of
+// a test or of the setup leaks them. When there is no pseudoterminal to use,
+// have_pty stays false and the tests return without checking anything.
+class LlvmLibcTcflushTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
+public:
+ int manager = -1;
+ int subsidiary = -1;
+ bool have_pty = false;
+
+ void SetUp() override {
+ LIBC_NAMESPACE::testing::ErrnoCheckingTest::SetUp();
+ have_pty = open_pseudoterminal_with_input();
+ if (have_pty)
+ ASSERT_ERRNO_SUCCESS();
+ else
+ LIBC_NAMESPACE::libc_errno = 0;
+ }
+
+ void TearDown() override {
+ if (subsidiary >= 0)
+ LIBC_NAMESPACE::close(subsidiary);
+ if (manager >= 0)
+ LIBC_NAMESPACE::close(manager);
+ LIBC_NAMESPACE::testing::ErrnoCheckingTest::TearDown();
+ }
+
+private:
+ bool open_pseudoterminal_with_input() {
+ manager = LIBC_NAMESPACE::open("/dev/ptmx", O_RDWR | O_NOCTTY);
+ if (manager < 0)
+ return false;
+
+ int unlock = 0;
+ unsigned int number = 0;
+ if (LIBC_NAMESPACE::ioctl(manager, TIOCSPTLCK, &unlock) != 0 ||
+ LIBC_NAMESPACE::ioctl(manager, TIOCGPTN, &number) != 0)
+ return false;
+
+ char path[32];
+ LIBC_NAMESPACE::snprintf(path, sizeof(path), "/dev/pts/%u", number);
+ subsidiary = LIBC_NAMESPACE::open(path, O_RDWR | O_NOCTTY);
+ if (subsidiary < 0)
+ return false;
+
+ if (LIBC_NAMESPACE::write(manager, "input\n", 6) != 6)
+ return false;
+
+ // The terminal hands input to the subsidiary side in the background, and a
+ // line is readable once the newline has been through.
+ struct pollfd pfd = {subsidiary, POLLIN, 0};
+ return LIBC_NAMESPACE::poll(&pfd, 1, 5000) == 1;
+ }
+};
+
+TEST_F(LlvmLibcTcflushTest, OutputFlushKeepsInput) {
+ if (!have_pty)
+ return;
+
+ ASSERT_TRUE(has_pending_input(subsidiary));
+ ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCOFLUSH), Succeeds(0));
+ ASSERT_TRUE(has_pending_input(subsidiary));
+}
+
+TEST_F(LlvmLibcTcflushTest, InputFlushDropsInput) {
+ if (!have_pty)
+ return;
+
+ ASSERT_TRUE(has_pending_input(subsidiary));
+ ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIFLUSH), Succeeds(0));
+ ASSERT_FALSE(has_pending_input(subsidiary));
+}
+
+TEST_F(LlvmLibcTcflushTest, BothFlushDropsInput) {
+ if (!have_pty)
+ return;
+
+ ASSERT_TRUE(has_pending_input(subsidiary));
+ ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIOFLUSH), Succeeds(0));
+ ASSERT_FALSE(has_pending_input(subsidiary));
+}
More information about the libc-commits
mailing list