diff --git a/fbclock/fbclock.c b/fbclock/fbclock.c index 47e8c392..58cc8082 100644 --- a/fbclock/fbclock.c +++ b/fbclock/fbclock.c @@ -15,6 +15,7 @@ limitations under the License. */ #include "fbclock.h" +#include // for static_assert in C11 #include // For O_* constants #include #include @@ -29,6 +30,20 @@ limitations under the License. #include #endif +// This file implements the seqlock, so it must see the real atomics. Defining +// FBCLOCK_CGO here would also leave the assert below comparing a type with +// itself. +#ifdef FBCLOCK_CGO +#error "FBCLOCK_CGO is only for cgo's view of the header, never for fbclock.c" +#endif + +// fbclock.h hands cgo a plain uint_fast64_t in place of atomic_uint64 so it can +// convert the shm structs. That is only sound while the two lay out the same. +static_assert( + sizeof(atomic_uint64) == sizeof(uint_fast64_t) && + alignof(atomic_uint64) == alignof(uint_fast64_t), + "atomic_uint64 must match its base type's layout; see FBCLOCK_CGO in fbclock.h"); + #if defined(__GNUC__) && !defined(__OPTIMIZE__) #define fbclock_debug_print(fmt, ...) \ do { \ diff --git a/fbclock/fbclock.go b/fbclock/fbclock.go index 69e663f0..25cd6def 100644 --- a/fbclock/fbclock.go +++ b/fbclock/fbclock.go @@ -22,6 +22,10 @@ package fbclock #cgo LDFLAGS: -lrt #cgo amd64 CFLAGS: -msse4.2 +// Scoped to this preamble on purpose: a #cgo CFLAGS define would also reach +// fbclock.c, which needs the real atomics. See FBCLOCK_CGO in fbclock.h. +#define FBCLOCK_CGO 1 + #include "fbclock.h" // @oss-only // @fb-only: #include "time/fbclock/fbclock.h" diff --git a/fbclock/fbclock.h b/fbclock/fbclock.h index 1df2291c..473624db 100644 --- a/fbclock/fbclock.h +++ b/fbclock/fbclock.h @@ -16,7 +16,19 @@ limitations under the License. #pragma once -#if defined(__cplusplus) && !defined(__clang__) +#include /* for proper fixed width types */ + +// Only the cgo preambles in fbclock.go and shmem.go define FBCLOCK_CGO. cgo +// builds its Go types from DWARF, and Go's debug/dwarf has no model for +// DW_TAG_atomic_type, which clang 21 emits where clang 19 folded the atomic +// into its base integer; every struct reachable from fbclock_lib then fails to +// convert. Go needs the size and field offsets of the shm structs; it must +// never touch seq or crc itself, so a plain base type serves it. fbclock.c +// static_asserts that the substitution is layout-identical. The Rust bindings +// solve the same problem with --opaque-type; see this directory's BUCK. +#if defined(FBCLOCK_CGO) +typedef uint_fast64_t atomic_uint64; +#elif defined(__cplusplus) && !defined(__clang__) #include typedef std::atomic_uint_fast64_t atomic_uint64; #else @@ -24,7 +36,6 @@ typedef std::atomic_uint_fast64_t atomic_uint64; typedef atomic_uint_fast64_t atomic_uint64; #endif -#include /* for proper fixed width types */ #ifndef __cplusplus #include /* for alignas in C; alignas is a keyword in C++ */ #endif diff --git a/fbclock/shmem.go b/fbclock/shmem.go index d041f7a7..5d5c78fd 100644 --- a/fbclock/shmem.go +++ b/fbclock/shmem.go @@ -21,6 +21,10 @@ package fbclock /* #cgo LDFLAGS: -lrt +// cgo compiles each preamble as its own translation unit, so every file that +// includes the header needs this, not just fbclock.go. See FBCLOCK_CGO there. +#define FBCLOCK_CGO 1 + #include "fbclock.h" // @oss-only // @fb-only: #include "time/fbclock/fbclock.h"