Skip to content

Commit 0d82a50

Browse files
etrclaude
andcommitted
Fix header_hygiene CI: skip pthread guards on libstdc++ in thread mode
CI on Linux failed `header_hygiene` because libstdc++ in -D_REENTRANT mode (set unconditionally by configure.ac) routes std::thread support through <bits/gthr-default.h>, which #includes <pthread.h> directly. That defines _PTHREAD_H once any STL container header (<memory>, <string>, <vector>, ...) is reached -- so the runtime sentinel reported a leak even though no libhttpserver public header pulls <pthread.h> in. This is the same STL implementation detail we already exempt for libc++ via _LIBCPP_VERSION; the original test comment claimed libstdc++ did not have this property, which was incorrect for thread-enabled builds. Extend the existing skip to also fire on libstdc++ in thread mode by testing _GLIBCXX_HAS_GTHREADS, and update the matching comment in Makefile.am so the asymmetry between the runtime sentinel and the preprocessor-grep gate (which already excludes pthread) is documented consistently. The guard still fires under STLs that don't route std::thread through pthread (e.g. MSVC's Microsoft STL). Verified locally: - macOS libc++: header_hygiene PASS (unchanged). - gcc:14 libstdc++ -D_REENTRANT: pre-fix the test reproduces the exact CI failure ("1 forbidden header(s) leaked"); post-fix it PASSes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0bfbe85 commit 0d82a50

2 files changed

Lines changed: 40 additions & 27 deletions

File tree

Makefile.am

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,23 @@ check-install-layout:
196196
# Cross-reference: keep HEADER_HYGIENE_FORBIDDEN in sync with the
197197
# #ifdef ladder in test/unit/header_hygiene_test.cpp.
198198
#
199-
# TASK-020 caveat (libc++): <pthread.h> is intentionally absent from
200-
# the forbidden list below. libc++ (Apple's default STL on macOS)
201-
# unconditionally includes <pthread.h> from any STL container header
202-
# (<vector>, <string>, <map>, etc.) via its
203-
# <__thread/support/pthread.h> backend. The resulting `# N "...pthread.h"`
204-
# line markers therefore appear in the preprocessed output even though
205-
# libhttpserver itself does not include <pthread.h>. The runtime
206-
# sentinel test/unit/header_hygiene_test.cpp keeps the pthread guard
207-
# but skips it when _LIBCPP_VERSION is defined, so the same invariant
208-
# is still enforced under libstdc++ and other STLs.
199+
# TASK-020 caveat (libc++ AND libstdc++ in thread mode): <pthread.h>
200+
# is intentionally absent from the forbidden list below. Both
201+
# mainstream STLs unconditionally pull <pthread.h> in from any STL
202+
# container header (<vector>, <string>, <map>, etc.) when threading
203+
# is enabled:
204+
# - libc++ (Apple's default STL on macOS) routes through
205+
# <__thread/support/pthread.h>.
206+
# - libstdc++ in thread-enabled mode (which is the default whenever
207+
# -D_REENTRANT is set, as configure.ac does) routes through
208+
# <bits/gthr-default.h>, which #include <pthread.h> directly.
209+
# The resulting `# N "...pthread.h"` line markers therefore appear in
210+
# the preprocessed output even though libhttpserver itself does not
211+
# include <pthread.h>. The runtime sentinel
212+
# test/unit/header_hygiene_test.cpp keeps the pthread guards but skips
213+
# them on both libc++ (_LIBCPP_VERSION) and libstdc++ in thread mode
214+
# (_GLIBCXX_HAS_GTHREADS), so the guards still fire on STLs that don't
215+
# route std::thread through pthread (e.g. MSVC's Microsoft STL).
209216
# ---------------------------------------------------------------------------
210217

211218
HEADER_HYGIENE_FORBIDDEN = microhttpd\.h|gnutls/gnutls\.h|sys/socket\.h|sys/uio\.h

test/unit/header_hygiene_test.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,20 @@
5959
// red states during M2-M5 -- the leaks must be removed in production
6060
// code, not here.
6161
//
62-
// TASK-020 caveat (libc++/Apple): libc++ (Apple's default STL)
63-
// unconditionally includes <pthread.h> from any STL container header
64-
// (<vector>, <string>, <map>, etc.) via its
65-
// <__thread/support/pthread.h> backend. That defines _PTHREAD_H even
66-
// when libhttpserver itself does not include <pthread.h>. This is an
67-
// STL implementation detail, not a libhttpserver leak. We therefore
68-
// skip the pthread guards on libc++ (detected via _LIBCPP_VERSION).
69-
// On libstdc++ and other STLs, the same containers do NOT pull in
70-
// <pthread.h> transitively, so the guards remain a meaningful leak
71-
// signal there.
62+
// TASK-020 caveat (libc++ AND libstdc++ in thread mode): both
63+
// mainstream STLs pull <pthread.h> in transitively from any STL
64+
// container header (<vector>, <string>, <map>, etc.) when threading
65+
// is enabled, so _PTHREAD_H / _PTHREAD_H_ being defined is an STL
66+
// implementation detail, not a libhttpserver leak:
67+
// - libc++ (Apple's default STL on macOS) routes through
68+
// <__thread/support/pthread.h>.
69+
// - libstdc++ in thread-enabled mode (which is the default whenever
70+
// -D_REENTRANT is set, as configure.ac does) routes through
71+
// <bits/gthr-default.h>, which #include <pthread.h> directly.
72+
// We therefore skip the pthread guards on both STLs (detected via
73+
// _LIBCPP_VERSION and _GLIBCXX_HAS_GTHREADS respectively). The guards
74+
// remain meaningful on STLs that don't use pthread for std::thread
75+
// (e.g. MSVC's Microsoft STL, which uses Win32 threading).
7276
//
7377
// Cross-reference: the same forbidden-header list is enforced via the
7478
// preprocessor-grep target `make check-hygiene` in the top-level
@@ -86,12 +90,14 @@ int main() {
8690
++leaks;
8791
#endif
8892

89-
// TASK-020: libc++ unconditionally drags <pthread.h> in from any STL
90-
// container header via its <__thread/support/pthread.h> backend, so
91-
// _PTHREAD_H / _PTHREAD_H_ defined on libc++ is not a libhttpserver
92-
// leak signal -- it is an STL implementation detail. Skip these guards
93-
// on libc++; keep them strict on libstdc++ and other STLs.
94-
#ifndef _LIBCPP_VERSION
93+
// TASK-020: both libc++ and libstdc++ (in thread-enabled mode, which
94+
// is the default whenever -D_REENTRANT is set) unconditionally drag
95+
// <pthread.h> in from any STL container header. _PTHREAD_H /
96+
// _PTHREAD_H_ being defined under either STL is an implementation
97+
// detail, not a libhttpserver leak. Skip these guards on both;
98+
// keep them strict on STLs that don't route std::thread through
99+
// pthread (e.g. MSVC's Microsoft STL).
100+
#if !defined(_LIBCPP_VERSION) && !defined(_GLIBCXX_HAS_GTHREADS)
95101
#ifdef _PTHREAD_H
96102
std::fprintf(stderr, "LEAK: <pthread.h> reached the consumer TU (glibc/musl guard _PTHREAD_H)\n");
97103
++leaks;
@@ -101,7 +107,7 @@ int main() {
101107
std::fprintf(stderr, "LEAK: <pthread.h> reached the consumer TU (macOS/BSD guard _PTHREAD_H_)\n");
102108
++leaks;
103109
#endif
104-
#endif // !_LIBCPP_VERSION
110+
#endif // !_LIBCPP_VERSION && !_GLIBCXX_HAS_GTHREADS
105111

106112
#ifdef GNUTLS_GNUTLS_H
107113
std::fprintf(stderr, "LEAK: <gnutls/gnutls.h> reached the consumer TU (guard GNUTLS_GNUTLS_H)\n");

0 commit comments

Comments
 (0)