147 Commits

Author SHA1 Message Date
CHEN Feng 81386ff632 testing: drop SO_REUSEADDR from the port-availability probe (#214)
PickAvailablePort()/PickAvailableEndpoint() probe a candidate port by
binding to it, but the probe socket set SO_REUSEADDR before binding
0.0.0.0:port. On macOS/BSD that lets the probe succeed even while another
process (e.g. a sibling test's server) already holds 127.0.0.1:port,
falsely reporting the port as free. The caller then binds its real server
to the same port and fails with EADDRINUSE.

This only surfaces under parallel test execution, where multiple tests
grab ports concurrently -- e.g. logging_test failing with "Cannot bind
socket to [127.0.0.1:5136]. : Address already in use" when run alongside
server_test/server_group_test/integration_test.

An availability probe must observe the port exactly as a fresh server
bind would, so it must not set SO_REUSEADDR. Without it, an occupied port
is correctly rejected and PickAvailablePort() retries another random
port. Verified with a deterministic repro (occupied 127.0.0.1:P; probe
with SO_REUSEADDR reports free, without it reports occupied) and by
stress-running the four port-grabbing rpc tests in parallel.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 10:47:53 +08:00
CHEN Feng 8833dfa279 base: never-destroy tls_queues to fix an exit-time mutex crash (#213)
* base: never-destroy tls_queues to fix an exit-time mutex crash

A static `MonitoredTimer` (and similar globals) calls
DeleteThreadOutOfDutyCallback in its destructor during static destruction, which
locks the `tls_queues` ThreadLocal's internal mutex. `tls_queues` was a plain
namespace static, so on macOS it could already be destroyed by then, and
std::mutex::lock() throws std::system_error EINVAL ("mutex lock failed") ->
std::terminate at process exit. monitoring_test reproduced this reliably
(all cases pass, then SIGABRT on shutdown).

Wrap it in NeverDestroyed, exactly as GetGlobalQueue() already does in the same
file, so the lock stays valid for the whole process lifetime. Leaking it is
harmless (the OS reclaims it at exit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* base: clang-format the tls_queues comment

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 08:32:47 +08:00
CHEN Feng 6320851cd6 thirdparty: make foreign_build.bld Starlark-parseable (#212)
blade-go's front-end is Starlark, which (unlike Python) has no generator
expressions or implicit string concatenation. Convert foreign_build.bld to the
equivalent Starlark forms so it can be include()'d there, with no behavior change
under Python Blade:

- generator expr -> list comprehension in the rpath ' '.join(...).
- adjacent-string-literal concatenation -> explicit '+' in dyld_path, the
  autotools configure command, and the cmake generate command (parenthesized so
  the trailing `% (...)` still applies to the whole concatenation).
- fix cmake_cc_library, which was dead-but-unparseable: it referenced undefined
  package_name/deps/with_packages. Give it deps/with_packages params, pass
  package_name=name and source_package through (mirrors autotools_cc_library).

Same Starlark-compat class as the cc_flare_library change in the build_rules.bld.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:23:35 +08:00
CHEN Feng 3a5c410c18 build: make cc_flare_library front-end-agnostic; upgrade vendored blade (#211)
Rewrite the two Python-only constructs in the cc_flare_library extension so it
parses under a Starlark front-end as well as Blade's (restricted) Python DSL,
with identical behaviour under Blade:

- `assert len(srcs) == 1, msg` -> `if len(srcs) != 1: fail(msg)`
- `deps is None` -> `deps == None`
- implicit adjacent-string concatenation -> explicit `+`

`isinstance` is kept (both front-ends provide it). This lets flare build under a
Go reimplementation of Blade (blade-go) without changing the current build.

Also upgrade the vendored blade (thirdparty/blade/blade.zip + GITVERSION) to
blade-build master, which adds the `fail()` builtin the change above uses.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:14:34 +08:00
CHEN Feng 71698ba232 thirdparty: pin AR/RANLIB in autotools foreign builds (fix macOS jemalloc link) (#210)
_configure passed CC/CXX from the resolved toolchain but let AR/RANLIB fall
through to PATH. On macOS, a stray GNU-format `ar` ahead of /usr/bin/ar (e.g.
Homebrew binutils / an llvm-ar in --format=gnu mode) builds a package's static
archive in GNU format -- with `/` and `//` index members -- which Apple's ld
rejects at link time:

  ld: archive member '/' not a mach-o file in '.../lib/libjemalloc.a'

This surfaced on je_server (jemalloc's libjemalloc.a is built by a direct
$(AR) crus Makefile rule, so it picks up the PATH ar; libtool-built archives
like libtcmalloc/jsoncpp/ctemplate were unaffected because libtool drives the
archiver consistently). The gperftools profiler archives hit the same GNU
format but are gated Linux-only, so they never reached a macOS link.

Pin AR/RANLIB the same way CC/CXX already are: force the system BSD
ar/ranlib on macOS (they emit a `__.SYMDEF` archive ld accepts), and honor
the toolchain ar elsewhere (GNU archives are fine for GNU ld). Passed both as
env and as autoconf precious-vars so the generated Makefile uses them
(verified: `AR = /usr/bin/ar`, archive members are `__.SYMDEF SORTED` + objects,
je_server links).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 22:44:52 +08:00
CHEN Feng be265c3307 rpc: de-flake Server.OverloadTest{,NoCreateSpecialMessage} (#209)
`ASSERT_EQ(100, succeeded)` over N racing calls against a 100-slot cap is
timing-dependent: the handler slept a fixed 2s, so on a busy/slow runner the
first sleepy handlers finish and free slots before all N calls are submitted,
letting more than the cap succeed (CI saw 300/400). Being a *fatal* assert, the
failure also skipped `gate->Stop()`/`server.Stop()`, leaving in-flight calls and
crashing the next test (SIGSEGV).

Make it deterministic: SleepyEchoService now *holds* its concurrency slot until
released (bumping an `admitted` counter on entry). Each test fires exactly the
cap (100) with a long deadline, waits until the cap is full, then floods the
rest with a short deadline -- all dropped, since the cap is held. Once those
drain, it releases the held calls: precisely the cap's worth succeed, no extra
slips into a freed slot, and nothing times out -- independent of runner speed.
Also switch to EXPECT_EQ so a failure can't skip teardown.

Verified: passes 10/10 under heavy CPU load (was consistently failing on the
busy CI runner); ~1.1s each (down from ~3.1s).
2026-06-28 06:41:00 +08:00
CHEN Feng 91a27b93e5 thirdparty/blade: bump vendored blade to v3.0.0 (dd0d2ae) (#208)
Blade 3.0.0 is released. Bump the vendored blade from 3100733 (2026-06-23)
to the 3.0.0 release commit dd0d2ae (2026-06-27): regenerated blade.zip via
dist_blade and updated GITVERSION.
2026-06-27 19:51:25 +08:00
CHEN Feng 8967316d93 object_pool: drop over-alignment of TLS LocalPoolDescriptor (UBSan) (#207)
`LocalPoolDescriptor` was `alignas(hardware_destructive_interference_size)` to
keep its hot `objects` head off neighbouring cache lines, but its only instances
live in thread-local storage (`GetLocalPoolDescriptor`). TLS does not honour
over-alignment (> max_align_t) on several platforms (notably darwin), so the
object was actually placed misaligned and every member access in `Get`/`Put`
was undefined behaviour. UBSan reports it as

  memory_node_shared.h: member access within misaligned address ... for type
  'LocalPoolDescriptor', which requires 64 byte alignment

and, because every allocation in flare goes through this pool, it aborts 21
flare/base tests under `--sanitizer=undefined` (crypto, buffer, monitoring,
object_pool, net, ...).

Since the over-alignment was never honoured, the intended false-sharing
isolation never happened -- so dropping it removes the UB at no real cost (the
placement was never aligned anyway) and keeps the existing TLS storage semantics
(the descriptor's storage must outlive its destructor, which poisons `current_`
so late `Put`s during thread teardown take the safe slow path). Real
cross-thread isolation here would need a different, persistent-aligned-storage
mechanism.

Verified: `flare/base/... --sanitizer=undefined` is all green (was 21 failing);
normal build unaffected.
2026-06-27 15:37:28 +08:00
CHEN Feng 6504d13b7b base: make heavy tests pass under sanitizers; fix latch_test data race (#206)
* base: make heavy tests pass under sanitizers; fix latch_test data race

Running `flare/base/...` under `--sanitizer=thread` surfaced one test-side data
race and three tests too heavy to finish under instrumentation:

- latch_test: `TEST(Latch, Torture)` runs `RunTest` on 10 threads, each ending
  with `std::cout << ...`; concurrent formatted output races the stream buffer.
  Serialize that line with a mutex.

- spinlock_test: 100 threads x 100k contended lock cycles -- under a sanitizer
  each contended acquire spins an unbounded, instrumented number of times, so
  the run takes many minutes (well past the test timeout). The brute-force
  volume is the race detector in a non-sanitized build, so keep it there and
  scale down (8 threads x 2k) only under ASan/TSan, following the existing
  `scheduling_group_test` pattern (FLARE_INTERNAL_USE_ASAN/TSAN).

- compression_test / view_test: their 10 MiB workloads (LargeSize, VariantSize's
  ~9.5k-size sweep, the noncontiguous-buffer searches) are slow to instrument
  and time out under parallel sanitizer runs. These are large-data, not
  brute-force-race, tests, so just shrink them unconditionally to sizes that
  still exercise the multi-block / cross-block paths (512 KiB, ~1 MiB, a coarser
  size sweep). The 2 GiB extreme stays covered by DISABLED_HugeSize.

Verified: `flare/base/... --sanitizer=thread` is all green; the normal build is
unaffected (full-size workloads still run there, except the unconditionally
shrunk compression/view).

* base/thread: add annotation dep to spinlock_test in BUILD.bazel

spinlock_test.cc now includes flare/base/internal/annotation.h (for the
ASan/TSan workload guard); the blade BUILD got the dep but the Bazel build
needs it too.
2026-06-27 12:53:27 +08:00
CHEN Feng 48d793e6ab base/internal: fix data race on JobQueue::node_id_ (TSan) (#205)
`JobQueue::node_id_` was a plain `int`, written by the owner thread in `Push`'s
periodic bookkeeping (`node_id_ = numa::GetCurrentNode()`) and read concurrently
by the flusher thread in `FlushDpcs` (`GetNode()` -> `BackgroundTaskHost::Queue`)
-- a data race. ThreadSanitizer reports it (size-4 read/write), aborting any
test that drives DPC, e.g. dpc_test and the monitoring tests (which flush via
`MonitoredTimer::FlushBufferedReports` -> `QueueDpc`).

The field is only a NUMA hint (which background queue to dispatch the flush to),
so a stale read merely picks a less-ideal queue. Make it a relaxed
`std::atomic<int>`: race-free, no ordering required, no measurable cost.
2026-06-27 12:37:10 +08:00
CHEN Feng eb4ba86a6a future: bound InlineExecutor recursion depth to avoid stack overflow (#204)
A long Future continuation chain resolved through the (default) InlineExecutor
recurses one frame per link -- SetBoxed -> Execute -> action -> SetBoxed -> ...
-- and overflows the stack. It shows up around ~10k links (FutureV2Test.Chaining
chains 10000) and triggers much sooner under AddressSanitizer, which inflates
stack frames: `AddressSanitizer: stack-overflow` in Core::SetBoxed.

Keep the historical re-entrant (synchronous, nested) execution -- some callers,
notably streaming RPC, rely on a completion running its continuation before
control returns -- but bound it: up to kMaxInlineDepth (128) re-entrant levels
run nested as before; past that a job is queued and drained by the outermost
frame (a trampoline), so the stack stays bounded while an unbounded chain still
completes. Realistic chains never reach the limit and behave exactly as before;
only a pathologically deep chain trades its deepest nesting for deferral, and a
linear chain's order is unchanged either way.

Verified: future tests pass with and without ASan (Chaining no longer
overflows), fiber suite and flare/base all green.

The thread_local state assumes an inline job does not suspend a fiber
mid-execution (these continuations run synchronously).
2026-06-27 09:40:41 +08:00
CHEN Feng 92f15f5412 thirdparty/blade: bump vendored blade to 3100733 (#203)
Catches flare's vendored blade up to blade-build master (from 34883163). What
this pulls in, user-visible:

- Cross-platform PGO: instrumentation (--profile-generate/--profile-use) and
  sample-based AutoFDO (--autofdo-generate/--autofdo-use) on gcc/clang, plus
  native MSVC (LTCG PGO / SPGO). Opt-in; no effect on flare's normal builds.
- --sanitizer now rejects an invalid combo (e.g. address,thread) at the command
  line instead of mid-build after the load/analyze pass and build-dir creation.
- Robustness: blade no longer crashes when git/svn is absent in a versioned
  workspace (SCM info degrades to 'unknown').
- MSVC C/C++ test coverage + coverage_config(exclude=...) (Windows-only; N/A to
  flare's Linux/macOS builds).

No flare BUILD/source changes needed; verified flare builds clean on the new
blade (base/encoding, a cc_test) and that --sanitizer=address still builds while
address,thread is rejected instantly.
2026-06-24 00:23:37 +08:00
CHEN Feng 83acfc7bf9 macOS: portable fiber stack under ASan + bump vendored blade (#202)
Two changes that together make `blade build/test --sanitizer / --coverage` work
on macOS:

- fiber: `GetMasterFiberStack()` used `pthread_getattr_np` (Linux/glibc only),
  which fails to compile on macOS clang under ASan (`FLARE_INTERNAL_USE_ASAN`).
  Add a macOS branch using `pthread_get_stackaddr_np` / `pthread_get_stacksize_np`
  (the stack grows down, so bottom = stackaddr - stacksize).
- thirdparty/blade: bump the vendored blade (blade.zip + GITVERSION) to
  blade-build 3488316, which includes the build-dir naming modernization, the
  fixes that stop `cc_check_undefined` from flagging the coverage/sanitizer
  runtime symbols (`__llvm_gcda_*`, `__ubsan_*`, the TSan `Annotate*` dynamic
  annotations, ...), and the --gprof/--coverage platform gating (no more dead
  `-pg` warnings on macOS).
- .gitignore: the new blade default build dir is `build_<profile>[_<variant>]`;
  ignore `/build_*/` (keeping the legacy `build{32,64}_*`).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 21:26:23 +08:00
CHEN Feng 92a08ae18b tracing: migrate opentracing-cpp → opentelemetry-cpp (API only) (#201)
* tracing: migrate opentracing-cpp -> opentelemetry-cpp (API only)

opentracing-cpp is deprecated/superseded by OpenTelemetry. flare ships no real
tracer -- it exposes a pluggable abstraction (TracingOpsProvider / QuickerSpan /
TracingOps) over the tracing-API types, with providers plugged in by users.
Re-base that abstraction on the opentelemetry-cpp **API** (header-only, v1.27.0)
and drop opentracing-cpp from both build systems.

Scope: API only -- no SDK/exporter, so no protobuf/grpc is pulled in and the
deliberately-pinned (abseil-free) protobuf 3.21.12 stays intact. The
abstraction and the opaque `bytes tracing_context` wire format are kept; only
the types/bodies change.

Source (flare/rpc/tracing/):
- tracing_ops_provider.h: re-typed virtuals; new flare-owned SpanStartOptions
  (parent SpanContext + SpanKind + timestamps + start attrs) replacing
  opentracing::StartSpanOptions/ChildOf. Extract -> std::optional<SpanContext>.
- tracing_ops.h/.cc: span_ is nostd::shared_ptr<trace::Span>; SetAttribute/
  AddEvent/End; SpanContext is a value type; buffered ops keep flare's owning
  variant and convert to (non-owning) AttributeValue only at flush.
- framework_tags.h -> constexpr std::string_view; string_view_interop.h ->
  ToStd/ToOtel (nostd<->std). Standard-tag table is a literal allowlist;
  span.kind moves from a tag to SpanStartOptions::kind.
- Call sites rpc_channel.cc / normal_connection_handler.cc updated; rpc_meta.proto
  comment updated (wire field unchanged).
- Tests/benchmark rewritten onto a recording fake otel::trace::Span (OTel's noop
  span can't be asserted on); integration test asserts span kind via the
  provider-recorded "span.kind" instead of an opentracing tag.

Build:
- blade: BLADE_ROOT vcpkg_config gains opentelemetry-cpp; thin wrapper
  //thirdparty/opentelemetry-cpp:api (refs opentelemetry_common -- the api
  component is header-only with no .a; that lib's include dir carries the api +
  absl headers). opentracing thirdparty dir removed.
- bazel: deps.bzl http_archive for opentelemetry-cpp 1.27.0 + api-only
  bazel/opentelemetry-cpp.BUILD (header-only, bundled nostd -> no abseil);
  patch_cmds drop the archive's nested BUILDs so the api headers can be globbed.
  opentracing BUILD/patch + .bazelignore/.gitattributes entries removed.

Verified: blade and bazel both build //flare/rpc/tracing/... and pass
tracing_ops_test + integration_test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: bazelignore thirdparty/opentelemetry-cpp (blade BUILD)

The repo-wide `bazel build ...` job parses every BUILD file; the blade thin
wrapper thirdparty/opentelemetry-cpp/BUILD uses blade-only syntax
(vcpkg#...:lib, visibility='PUBLIC'). Add it to .bazelignore (the opentracing
entry was removed but its replacement wasn't added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* tracing: clang-format (v15) the migrated files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: drop accidentally-committed .claude/ tooling files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* doc(tracing): note provider ABI contract for OpenTelemetry nostd types

The TracingOpsProvider interface uses OpenTelemetry API types
(nostd::shared_ptr<Span>, nostd::string_view, AttributeValue) directly.
The nostd:: backing is a compile-time choice (bundled / std via
OPENTELEMETRY_STL_VERSION / abseil) and the three are mutually
incompatible types, so a provider must be built with the same
OpenTelemetry configuration as flare. flare leaves the default nostd
in place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: gitignore .claude/ (Claude Code local tooling)

Prevents accidentally re-committing .claude/settings*.json (cf. review
comments on PR #201).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* tracing: address PR #201 review follow-ups (non-blocking)

- forcibly_sampled now routes `flare.sampling_priority` through the
  provider's `SetFrameworkTag` translation (before `IsSampled`) instead of
  a raw `SetAttribute`, keeping the `flare.`-prefixed tag consistent with
  every other framework tag and letting providers honor the force-sample
  signal. Add `ext::kSamplingPriority`.
- An empty log key (single-arg `AddTracingLog`) now materializes as the
  default event name "log" so backends that group/display by event name
  don't render a blank one; document the per-log-event mapping.
- Guard `QuickerValue`'s reliance on P0608R3 (C++20) with a static_assert
  so a future standard downgrade can't silently bind `const char*` to bool.
- Reset `DummySpan`'s inline-static tags/logs at the start of the recording
  test so assertions can't observe leftovers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 00:36:01 +08:00
CHEN Feng 0e384ab787 fiber tests: sync waitable_test exclusive to bazel; cap fiber count under sanitizers (#199) (#200)
Two small, independent test tweaks surfaced while triaging the fiber sanitizer
gaps (#199):

- waitable_test is marked `exclusive = True` in the blade BUILD but was missing
  `tags = ["exclusive"]` in BUILD.bazel. Sync them -- it's a heavy test that
  shouldn't run concurrently with others.

- scheduling_group_test spins up `min(max_map_count/4, 131072)` fibers. Under
  ASan/TSan that's both very slow (well past the test timeout) and memory
  hungry. Cap it to 4096 when a sanitizer is active (via FLARE_INTERNAL_USE_ASAN
  / _TSAN); a smaller count still exercises the scheduling paths. Non-sanitized
  builds are unchanged (still 131072).

Note: these don't make the heavy tests sanitizer-clean on their own -- both
still hit the use-after-poison tracked in #199 (a separate, deeper issue in the
ASan fiber-switch annotations).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:06:07 +08:00
CHEN Feng 8396477144 ci: add ASan/TSan fiber regression lane (#134, #196) (#198)
* ci: add ASan/TSan fiber regression lane (#134, #196)

The fiber-switch sanitizer fixes (#134 ASan/TSan SEGV, #196
detect_stack_use_after_return) had no automated coverage -- flare's CI is
blade-based and never built under sanitizers, so these bugs sat undetected.

Add a Bazel + clang job that builds and runs a curated set of fiber tests
under both `--config=asan` and `--config=tsan` (matrix). Also add a `tsan`
config to .bazelrc (mirroring the existing `asan` one).

Scope: a curated list of fiber tests that pass cleanly under both sanitizers,
which is enough to catch any regression of the fiber-switch fixes. The full
`//flare/fiber/...` is intentionally NOT used -- it has unrelated sanitizer
problems out of scope here: curl fails to foreign_cc-build under -fsanitize;
a few sync-primitive tests (scheduling_group/latch/waitable) have their own
ASan/TSan failures; and some tests exceed even a raised timeout under TSan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: download bazelisk to a temp path (repo has a bazel/ dir)

The install step did `curl -o bazel`, but flare tracks a `bazel/` directory,
so curl failed with exit 23 (can't write over a directory) before any test
ran. Download to $RUNNER_TEMP instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: narrow sanitizer_fiber to a fast core set, tighten timeout

The 15-test set was fine under ASan (~4 min) but pathologically slow under
TSan on shared 2-4 core runners (the heavier sync-primitive tests). Narrow to
a focused set that exercises fiber create/resume/switch -- enough to guard the
switch fixes, since a broken switch crashes every fiber test on first resume.
Drop --test_timeout to 300s so a straggler fails fast instead of stalling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: pin sanitizer_fiber to clang-17 (clang-18 TSan header clash)

flare's annotation.h hand-declares __tsan_* interface symbols (mutex flags,
fiber no_sync) that collide with clang-18's <sanitizer/tsan_interface.h>
('non-static declaration follows static declaration'), breaking the TSan
build. That's a separate pre-existing issue; pin the lane to clang-17 (the
toolchain the fiber-switch fixes were verified on) so the regression guard is
green. clang-18 TSan-header compatibility tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: force clang-17 via bazel --repo_env (update-alternatives ignored)

The previous attempt used update-alternatives to point /usr/bin/clang at
clang-17, but bazel's autodetected toolchain still resolved clang-18
(/usr/lib/llvm-18/bin/clang) from the runner's PATH, so the TSan build kept
hitting the clang-18 tsan_interface.h clash. Configure the toolchain compiler
explicitly with --repo_env=CC=/usr/bin/clang-17 (and CXX), and drop
--config=llvm (which forced the generic 'clang').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: make clang-17 install non-interactive (apt hang)

A TSan-leg runner got stuck in the apt install step (the ASan leg's identical
step passed) -- a needrestart/debconf interactive hang. Set
DEBIAN_FRONTEND=noninteractive + NEEDRESTART_MODE=a, sudo -E, and
--no-install-recommends so the install can't block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: use preinstalled clang-17, stop apt-installing it (apt hangs)

Multiple runner instances hung in 'apt-get install clang-17'. ubuntu-24.04
ships clang 16/17/18 preinstalled, so locate clang-17 directly (probe
/usr/bin/clang-17 and /usr/lib/llvm-17/bin/clang) and point bazel at it via
--repo_env. No apt install -> no hang; if clang-17 were ever absent the step
fails fast instead of stalling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fiber/ci: build TSan via <sanitizer/tsan_interface.h>; use default clang in CI

annotation.h hand-declared the __tsan_* symbols (fiber API, mutex API + flag
constants, acquire/release). Those collide with compiler-rt's
<sanitizer/tsan_interface.h> ("non-static declaration follows static
declaration") whenever that header is in the translation unit under
-fsanitize=thread, which broke the TSan build on the CI runners (both the
default clang-18 and the runner's clang-17 hit it). It is NOT version-specific.

Include the canonical header directly instead of redeclaring. FLARE_INTERNAL_
USE_TSAN is only defined under -fsanitize=thread, and any toolchain providing
that flag ships this header (verified: clang-17, and gcc-13 which also defines
__SANITIZE_THREAD__ and exposes the fiber/mutex API), so it's always available.

With the build no longer tied to a clang version, drop the CI lane's clang-17
pinning (which needed an apt install that intermittently hung) and just use the
runner's default clang via --config=llvm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:41:17 +08:00
CHEN Feng 88f15ec849 fiber: fix TSan SEGV on fiber switch by stripping func-entry/exit hooks (#134) (#197)
Under TSan with clang, `bazel test --config=tsan //flare/fiber:*` crashes on
the first fiber resume:

    ThreadSanitizer: SEGV ... in __tsan_func_entry
      <- FiberProc (first instruction of a freshly-started fiber)

Root cause (traced with gdb): TSan keeps a per-fiber "shadow stack" of call
frames; `__tsan_func_entry` pushes and `__tsan_func_exit` pops it.
`flare::internal::tsan::SwitchToFiber` calls `__tsan_switch_to_fiber`, which
makes the *target* fiber's shadow stack active. But that call sits inside an
instrumented function, so the function's own injected `__tsan_func_exit` runs
*after* the switch -- popping the just-activated, and for a brand-new fiber
empty, shadow stack. shadow_stack_pos underflows to base-8; the next
`__tsan_func_entry` (at FiberProc) writes below the mapped region and SEGVs.

Confirmed: `-fno-sanitize-thread-func-entry-exit` (which removes those hooks)
makes it pass. Crucially, plain `[[no_sanitize("thread")]]` does NOT fix it --
it disables the data-race read/write checks but keeps the func-entry/exit
hooks, so the underflow remains.

Fix: add `FLARE_INTERNAL_DISABLE_SANITIZER_INSTRUMENTATION`
(`[[clang::disable_sanitizer_instrumentation]]`, which strips ALL hooks
including func-entry/exit) and apply it to the functions that bracket a raw
context switch: the `SwitchToFiber` wrapper, `FiberEntity::Resume`,
`FiberEntity::ResumeOn`, and the `FiberProc` entry trampoline. This keeps full
TSan instrumentation (and call stacks in race reports) everywhere else, unlike
the global compile flag. GCC has no such attribute (only Clang), so the macro
expands to nothing there -- GCC builds are non-sanitized, and TSan-with-fibers
is a Clang-only configuration in practice.

Verified with clang-17 under `--config=tsan` on both x86_64 and aarch64:
future_test / fiber_test / timer_test / this_fiber_test all pass. ASan (#196)
and the normal non-sanitized build are unaffected.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 01:56:20 +08:00
CHEN Feng ff5e9ad540 fiber: fix ASan SEGV on fiber switch by disabling detect_stack_use_after_return (#134) (#196)
Under ASan with clang, `bazel test --config=asan //flare/fiber:*` crashes on
the first fiber resume:

    SEGV in flare::internal::asan::StartSwitchFiber (annotation.h)
      <- FiberEntity::Resume() <- ResumeOn() <- FiberProc()

Root cause: ASan's `detect_stack_use_after_return` (on by default since
Clang 11) gives each function its own "fake stack". flare hand-switches
between pooled fiber stacks; ASan's fake-stack bookkeeping cannot be followed
across that manual switch, so `__sanitizer_start_switch_fiber` faults. This is
independent of CPU arch (reproduced on both x86_64 -- the original report --
and aarch64) and of fiber stack size.

Fix: default `detect_stack_use_after_return=0` for any binary that links the
fiber runtime, via `__asan_default_options()` in fiber_entity.cc (always in the
fiber lib's link closure, so it isn't dropped). The ASAN_OPTIONS environment
variable still overrides it, and every other ASan check stays enabled. Also set
it in `.bazelrc`'s `build:asan` test_env so it's explicit for `bazel test` and
survives a user-provided `__asan_default_options`.

Verified with clang-17: with the env unset, future_test / fiber_test /
timer_test / this_fiber_test all pass under `--config=asan`.

Note: this addresses the ASan half of #134 only. The TSan crash has a
different root cause (TSan shadow handling for switched fiber stacks, not a
config toggle) and remains open.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:08:27 +08:00
CHEN Feng 3c103bd249 bazel/blade: drop hardcoded well_known_protos; auto-discover from vcpkg protobuf (#195)
blade-build#1339 (merged) makes blade auto-discover the well-known protos
(google/protobuf/*.proto) from the protobuf include tree when
proto_library_config.well_known_protos is empty. With the vcpkg#protobuf
provider that resolves to the installed include/, so the list need not be
hand-maintained in BLADE_ROOT and stays correct across protobuf versions.

Empty the list (blade falls back to a built-in canonical set if the include
tree is ever unresolvable) and vendor the blade.zip built from the merged
blade master. Verified: building flare/rpc/... still passes
--direct_dependencies with the same 12 google/protobuf protos, now discovered.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 23:28:26 +08:00
CHEN Feng 8c396a89d3 ci(test): mark overload/streaming RPC tests exclusive (#193)
server_test (overload), proto_over_http_protocol_streaming_rpc_test and
std_protocol_streaming_rpc_test stress the whole machine; under concurrent CI
load they crash (SIGSEGV/SIGABRT) or time out nondeterministically though they
pass in isolation. Mark them exclusive so blade runs them alone.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 09:06:10 +08:00
CHEN Feng 5a0132028e docs(thirdparty): mark openssl/nghttp2/curl migrated; only jsoncpp left (#191)
The curl cluster (#190) moved openssl (->3.x), nghttp2 and curl to vcpkg, so
they move from the blocked table to the migrated table. jsoncpp is the sole
remaining #179 lib (0.x->1.x API change).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 08:32:33 +08:00
CHEN Feng ed29968f5a thirdparty: migrate openssl/nghttp2/curl to vcpkg (#179 curl cluster) (#190)
* thirdparty: migrate openssl/nghttp2/curl to vcpkg (#179 curl cluster)

vcpkg has no openssl 1.1.1, so this upgrades the cluster to 3.x and migrates all
three off foreign builds. Consumers' //thirdparty/{openssl,nghttp2,curl} targets
are unchanged (thin vcpkg wrappers).

- openssl -> vcpkg 3.x. flare's crypto/init code adapted for 3.x:
  - flare/io/util/openssl.cc: gut the obsolete OpenSSL-1.0.x ceremony
    (SSL_library_init, ERR_load_*, ENGINE_*, CRYPTO_set_locking_callback /
    CRYPTO_num_locks). OpenSSL 1.1+ auto-initializes and is internally
    thread-safe, so both entry points are now empty no-ops.
  - flare/base/crypto/{md5,sha}: -Wno-deprecated-declarations -- the low-level
    MD5/SHA/HMAC APIs are deprecated in 3.x (provider/EVP migration) but still
    valid until 4.0, so suppress rather than rewrite to EVP.
- nghttp2, curl -> vcpkg (API-stable version bumps). curl built with the openssl
  + http2 features; its macOS frameworks (SystemConfiguration/Security/
  CoreFoundation/CoreServices) link via blade's new pkg-config -framework
  support. Wrapper declares the sibling vcpkg deps (openssl/nghttp2/zlib).
- thirdparty/blade/blade.zip: rebuilt with that -framework support.
- Drop the vendored tarballs and patches (openssl/curl/nghttp2, curl dyld patch).

Verified: full `blade test flare/...` green on macOS (crypto/ssl/curl/cos all
pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* thirdparty/openssl: drop redundant #dl/#pthread

blade now auto-propagates libcrypto's pkg-config system libs (Libs.private:
-ldl) via the #1322 mechanism, and flare links pthread globally on Linux
(cc_config). So the wrapper's explicit #dl/#pthread are redundant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: checkout the pinned vcpkg baseline (not just fetch)

Fetching only the baseline commit let 'git show <baseline>:versions/baseline.json'
resolve the baseline version, but vcpkg reads the per-port versions/*.json from
the working tree -- still the runner's stale HEAD. For ports whose baseline
version is newer than that HEAD (openssl 3.6.3, nghttp2 1.69, curl 8.20) vcpkg
errors 'no version database entry'. Checkout the baseline so the whole version
DB / ports tree is consistent (matching local dev).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 08:09:28 +08:00
CHEN Feng 5fb5dbbbf0 thirdparty/protobuf: fix proto_test gtest include after vcpkg migration (#189)
proto_test.cc included the old in-tree full path
"thirdparty/googletest/gtest/gtest.h", which no longer resolves after gtest
moved to vcpkg (#187) -- only gtest_prod.h remains in-tree; gtest headers now
come from vcpkg as "gtest/gtest.h" via the cc_test's gtest_libs include path.
ubuntu-24.04 caught it on a cold build64_release; macOS/22.04 passed on cached
objects. Switch to the standard "gtest/gtest.h".

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 03:01:58 +08:00
CHEN Feng ee31d650b0 docs(thirdparty): update vcpkg migration status (#188)
- gtest is migrated (#187) -> move to the ✅ table; note *_main libs resolve
  under vcpkg's lib/manual-link/ and the UnitTest-registry singleton is handled
  by the now-default 'auto' linkage.
- Reflect blade's new default linkage='auto' (cc_library-like): singletons get
  one shared instance with no per-port annotation; only non-default choices
  (benchmark 'static', link_all_symbols/include_prefix/cmake_options) are spelled
  out. Drop the explicit linkage='auto' from the gflags/glog/yaml-cpp/protobuf
  rows; correct the yaml-cpp note (the old "wrapper .dylib exports" story was
  wrong) and add the protobuf descriptor-pool-singleton rationale.
- Record the confirmed non-migratable libs: opentracing-cpp (vcpkg 1.6.0's
  variant.hpp uses std::result_of, removed in C++20 — needs the foreign build's
  patches), and blake3/rapidxml (in-tree vendored, not foreign builds).

No code/BUILD changes: ctemplate and opentracing were evaluated for vcpkg this
round and confirmed unmigratable, so they stay foreign.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 02:35:33 +08:00
CHEN Feng 158f72be83 thirdparty: migrate googletest to vcpkg; adopt blade default 'auto' linkage (#187)
- googletest: thin wrappers over vcpkg gtest (gtest/gtest_main/gmock/gmock_main,
  pinned 1.17.0); drop the vendored tarballs and generate_dynamic.patch. gtest's
  *_main libs come from vcpkg's lib/manual-link/ (resolved by the updated blade).
- BLADE_ROOT: update blade.zip to the build with default 'auto' linkage + the
  manual-link fix. With 'auto' the default, the explicit linkage='auto' on
  protobuf/gflags/glog/yaml-cpp/gtest is now redundant and removed; only the
  non-default choices remain (benchmark -> 'static' as it has no shared build;
  link_all_symbols / include_prefix / cmake_options where needed).
- BLADE_ROOT: drop the hardcoded vcpkg `root` path -- blade reads $VCPKG_ROOT,
  else finds vcpkg on PATH (portable; CI uses the runner's vcpkg).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 02:15:29 +08:00
CHEN Feng a1678b2278 thirdparty: migrate benchmark to vcpkg (pinned 1.7.1) (#186)
Replace the vendored google/benchmark 1.5.3 foreign build with a thin
vcpkg wrapper. Consumers keep depending on //thirdparty/benchmark:{benchmark,
benchmark_main}; cc_config.benchmark_libs / benchmark_main_libs are unchanged.

Pinned to 1.7.1: 1.8.0 deprecated benchmark::DoNotOptimize(const T&), which
flare's *_benchmark.cc use heavily, and flare builds gcc with
-Werror=deprecated-declarations (clang/macOS doesn't trigger it). 1.7.1 is the
last series before that deprecation.

vcpkg ships benchmark at its native include/benchmark/ layout, so no
include_prefix is needed. Drops the vendored tarball and the
remove_unused_var.patch.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 22:47:12 +08:00
CHEN Feng 7b68301df9 thirdparty: migrate yaml-cpp to vcpkg (#185)
yaml-cpp 0.6.3 -> vcpkg 0.9.0. linkage=auto + link_all_symbols (whole-archive the static .a into the wrapper shared lib so it exports symbols -- the real cause of the convert_to_map "ABI" undefined). thirdparty-only.
2026-06-17 15:57:21 +08:00
CHEN Feng ff0449e6dd thirdparty: upgrade protobuf to vcpkg 3.21.12 (last abseil-free) (#184)
Migrate protobuf from vendored 3.4.1 to vcpkg 3.21.12 (last release before Abseil became mandatory). vcpkg protoc + libprotobuf; source fixes for the 3.4->3.21 API (cpp_helpers.h, duplicate ext registration, Status::ToString); remove the ~47MB vendored 3.4.1 tree. Both blade and bazel builds + proto/RPC tests pass; protobuf install is abseil-free.
2026-06-17 13:56:31 +08:00
CHEN Feng 2bfd54cdea thirdparty/blade: update blade.zip — print progress without cursor control (#183)
Update vendored blade to master 168ce01 (blade-build#1334): print ninja status lines when the terminal has no cursor control (CI/pipe), keeping builds observable. Honors --quiet, keeps the completion summary.
2026-06-17 09:26:10 +08:00
CHEN Feng 39a8af2b6e bazel: silence macOS "ignoring duplicate libraries" warnings (#182)
Gate flare-own -lpthread/-ldl out on macOS (no-ops in libSystem) via select(), and add a macOS-only -Wl,-no_warn_duplicate_libraries (target + host links, auto-applied via enable_platform_specific_config) for the residual -lpthread/-lm from external deps. build_on_macos duplicate-library warnings: 128 -> 0. Closes #181.
2026-06-17 08:07:42 +08:00
CHEN Feng 55d97852f7 thirdparty: migrate fmt/gflags/glog/zlib/lz4/zstd/snappy/xxhash to vcpkg (#180)
Migrate eight third-party libraries (fmt, gflags, glog, zlib, lz4, zstd, snappy, xxhash) from in-tree foreign builds to vcpkg, via blade native vcpkg support. Bundles the matching blade.zip (proto import-root + vcpkg protoc + transitive Requires linking + textual_hdrs) and the CI/Bazel adjustments (pinned vcpkg baseline; xxhash externalized for Bazel).
2026-06-17 07:17:10 +08:00
CHEN Feng 24ab46abc2 thirdparty/blade: upgrade blade core and rewrite launcher for macOS (#178)
Update the vendored blade.zip (git-lfs) to the current build and rewrite
the `blade` wrapper to locate the core next to the script and run it with
a suitable Python interpreter, adding macOS support alongside Linux.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:49:53 +08:00
CHEN Feng 092eb31166 thirdparty/gtest: build both static and shared libs to fix empty test registries on macOS (#177)
On macOS shared-library builds, several `:main`-based tests
(http_mock_test, cos_mock_test, hbase_channel_test) ran zero test cases
and printed "This test program does NOT link in any test case", even
though their sources define real TEST()s.

Root cause: gtest was static-only. Release builds default to
dynamic_link, but with no libgtest.dylib the dynamic label fell back to
the static archive, which then got force_loaded (link_all_symbols) into
every mock/main dylib. Each dylib ended up exporting its own copy of
gtest's UnitTest registry singleton, so TEST() registration bound to one
copy while RUN_ALL_TESTS() (in libmain.dylib) read another. Whether a
test tripped this depended purely on dylib link order.

Fix: ship a shared libgtest so the registry is a single process-wide
instance. googletest's CMake keys solely off BUILD_SHARED_LIBS and emits
either static or shared per configure, so a single pass can't produce
both. Add an optional second configure+install pass to cmake_build
(`shared_cmake_options`) and use it for googletest: the static pass keeps
libgtest.a for static_link consumers, the shared pass adds libgtest.dylib
for dynamic_link ones. No source patch, so nothing to re-port on the next
gtest bump (the stale generate_dynamic.patch was written for 1.10 and no
longer applies to 1.17).

Verified: both .a and .dylib are produced; the three tests now link
libgtest.dylib and pass (8/3/2); ordinary gtest_main tests still pass;
full //flare/... build links clean.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:39:09 +08:00
CHEN Feng 4c1e391d88 docs: code-verified fixes across remaining docs + fill protocol/{http,new}.md (#176)
* docs(flare): code-verified fixes across remaining docs + fill protocol/{http,new}.md

Surgical fixes verified against current source:
- fiber.md, logging.md: flare::Async → flare::fiber::Async (no such alias at top level)
- io.md: malformed link io/../../io/detail/watchdog.h → ../io/detail/watchdog.h
- intro-http.md: HttpResponse::Status_OK → HttpStatus::OK (enum lives in net/http/types.h)
- rpc-form.md: typo Mssage → Message
- testing.md: stale namespace comment flare::testing::detail → example
- option.md: SomeParer → SomeParser, XxxOption>...> → XxxOption<...>,
  StringToJsonParser → JsonParser (real name in base/option/json_parser.h)
- coding-comparison.md: read.Read() → reader.Read() (variable mismatch)
- protocol/protocol-buffers.md: EnableCompression → SetCompressionAlgorithm
  (server controller has only the latter); max_queueing_delay →
  max_queueing_delay_ms; flag accept_request_raw_bytes as TODO (still
  commented out in rpc_options.proto:60 and std_protocol.cc:198)
- protocol.md: typo 内置协义 → 内置协议
- README.md: index orphans buffer.md / coding-comparison.md / gdb-plugin.md
  added so they show up in the docs ToC

Filled previously-empty stubs:
- protocol/http.md: describes HTTP-as-wire-protocol entry points
  (Http11Protocol, HttpService, HttpRequest/Response, HttpStatus) and
  delegates user-facing usage to intro-http.md
- protocol/new.md: step-by-step guide for adding a new protocol —
  StreamProtocol / Message / Controller / StreamService surfaces,
  FLARE_RPC_REGISTER_*_STREAM_PROTOCOL* registration macros, and a
  reference table of existing implementations (StdProtocol, Http11Protocol,
  HbaseClient/ServerProtocol, RedisProtocol)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: fix two dead links surfaced by markdown-link-check

- logging.md: CnTransGroup/EffectiveModernCppChinese was deleted upstream
  (HTTP 404 on the repo itself). Drop the dead URL but keep the textual
  reference to "Effective Modern C++" Item 30.
- protocol/protocol-buffers.md:249: relative path was missing the
  "protocol/" segment — ../../rpc/protobuf/rpc_meta.proto →
  ../../rpc/protocol/protobuf/rpc_meta.proto (matches the three other
  correct refs in the same file).

Both links pre-existed; the modified-files-only link checker only
surfaced them because this PR touched those two files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 13:48:32 +08:00
CHEN Feng 7fb4c8f0d8 docs(flare): verify and fix small issues across doc/ (#175)
* docs(flare): verify and fix small issues across docs/

Pure-mechanical cleanup pass through the remaining flare/doc/ files.
Every change is verified against the actual code referenced.

* `profiler.md`: title typo ``Profiller -> Profiler``; ``flag::Server``
  -> ``flare::Server`` in the CPU profiler section.

* `async.md`: ``Futrue`` typo -> ``Future``; link ``Async`` to its real
  header ``flare/fiber/async.h`` instead of leaving it as bare text.

* `controller.md`: replace "see source code comments" with concrete
  header links (``rpc_client_controller.h``, ``rpc_server_controller.h``,
  ``rpc_controller.h``); link the "tracing" reference to ``tracing.md``.

* `scheduling-group.md`: replace the ``...`` placeholder in the
  per-scheduling-group resources list with two concrete entries that
  exist in the code (object pool's ``memory_node_shared`` bucketing
  per NUMA node, monitoring's local-accumulate + cross-group aggregate
  reporting).

* `gflags.md` / `buffer.md`: add the missing ``[返回目录]`` footer
  that every other doc has. ``buffer.md`` also had a stray triple
  ``)))`` in the last code sample -- removed.

* `performance-guide.md`: fix broken header path
  ``../base/spinlock.h`` -> ``../base/thread/spinlock.h``. The file
  was moved into the ``thread/`` subdirectory at some point and the
  doc never followed.

* `rpc-log-and-dry-run.md`: fix two weird relative paths that
  contained nested ``../`` segments and a literal ``binlog/`` prefix
  that doesn't exist
  (``binlog/../../rpc/binlog/dumper.h`` -> ``../rpc/binlog/dumper.h``,
  ``../binlog/../rpc/binlog/dry_runner.h`` -> ``../rpc/binlog/dry_runner.h``).

No content rewrites; only typos, broken refs, and missing footers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(controller) + ci(link-check): fix #175 CI failures

Two issues link-check surfaced on this PR:

* controller.md referenced a non-existent ``flare/rpc/rpc_controller.h``.
  Actual hierarchy: ``RpcClientController`` and ``RpcServerController``
  both inherit from ``protobuf::RpcControllerCommon`` (no shared
  ``RpcController`` header). Replace the wrong link with a plain text
  mention of the common base class.

* performance-guide.md links to cppreference / stackoverflow /
  linux.die.net all 403'd against the link-check user agent. These
  URLs aren't broken in any normal browser; the docs were untouched
  in this PR but ``check-modified-files-only=yes`` re-scanned the
  file. Add the three domains to ``ignorePatterns`` with comments
  documenting the reason. The existing pattern for bazel.build set
  the precedent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci(link-check): add dev.mysql.com to UA-filter ignore list

Same shape as cppreference / stackoverflow / linux.die.net: the docs
URL returns 403 to the action's User-Agent but works in any browser.
Surfaced by rpc-log-and-dry-run.md re-scanning under this PR's edits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 01:20:06 +08:00
CHEN Feng d8d40cf461 docs(flare): fill in nslb.md, timer.md; refresh timestamp.md (#174)
* docs(flare): fill in nslb.md, timer.md; refresh timestamp.md for current impl

Three docs got attention:

* `nslb.md` was a 4-line stub. Filled in with the Channel -> NameResolver
  + LoadBalancer / MessageDispatcher data flow, registry mechanism for
  custom name schemes, the two built-in load balancers (RoundRobin /
  ConsistentHash), and the MessageDispatcher-integrated path for
  routing services that bundle resolution + balancing (CL5, Polaris, ...).

* `timer.md` was also a 4-line stub. Covers the fiber-side API
  (`SetTimer` / `KillTimer` / `SetDetachedTimer`, the `[[nodiscard]]`
  contract), the `[this_fiber::SleepFor]` blocking-wait pattern, the
  TimeKeeper for framework-internal periodic work, the TimerWorker
  heap-based implementation, scheduling-precision expectations, and
  the fire-and-forget cancellation semantics.

* `timestamp.md` was rewritten to match the current implementation:

  - `ReadSteadyClock` / `ReadSystemClock` no longer bypass libstdc++.
    Section now reflects that they delegate to `std::chrono::*::now()`
    directly and explains why the old hand-rolled `clock_gettime`
    path was removed (clock-epoch mismatch on macOS broke
    `TimerWorker::cv_.wait_until()`; the CentOS 6 / tlinux 1.2
    self-built-GCC problem the old impl worked around is no longer
    relevant on any modern toolchain).
  - Coarse clock section still accurate; description reworked to
    show the atomic-load read path more clearly.
  - TSC section preserves the existing technical content but adds the
    "invariant TSC is now de facto standard" caveat and ties the
    `DurationFromTsc` overflow warning to the long-duration case
    more visibly.
  - Added a top-of-file simple selection guide table and a
    decision-tree at the bottom -- this is the part most readers
    actually need.

No code changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci(link-check): ignore intel.com PDF UA-filtering

Intel CDN's PDF endpoint UA-filters markdown-link-check to 403 even
though the documents are reachable from any browser. Without this
ignore, the link-check fails any PR that touches a doc referencing
the canonical Intel whitepaper -- which is what just happened on
#172's timestamp.md re-shape.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(timestamp): use kib.kiev.ua mirror for Intel TSC whitepaper

Intel's CDN UA-filters the link checker to 403; the kib.kiev.ua x86
documentation mirror serves the same Intel doc 333159 without the
filter, so we can verify the link in CI without an ignore-pattern
escape hatch.

Reverts the ``^https?://www\.intel\.com/.*\.pdf`` ignore added in the
previous commit on this branch -- no longer needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 00:13:29 +08:00
CHEN Feng 356df6bb30 ci: skip heavy workflows on markdown-only changes (#173)
Adds `paths-ignore: ['**/*.md']` to the push/pull_request triggers of:

  - build-and-test.yml  (blade build + test, longest job at ~25 min)
  - bazel-build.yml     (bazel build + test, second-longest at ~15 min)
  - clang-format-check.yml (only inspects C/C++/Protobuf sources)

GitHub Actions skips the workflow only when *every* changed file matches
the ignore pattern, so mixed PRs (e.g. a code fix that also touches a
README) still get full coverage. Pure docs/README PRs -- like the
README rewrite -- now only run markdown-link-check, which is what
actually verifies them.

Saves ~25 min of CI wall time per pure-docs PR. The markdown-link-check
workflow keeps its unconstrained trigger; documentation correctness
still gets verified end-to-end.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 23:21:35 +08:00
CHEN Feng 362e00a180 docs(readme): update for C++20 + macOS support, retranslate zh/en (#172)
* docs(readme): update for C++20 + macOS support, retranslate zh/en

Bring both READMEs in line with the project's current state:

* C++ badge upgraded from a generic ``C++`` to ``C++20`` to match the
  actual standard the project compiles with (``-std=gnu++2a``).
* Platform badge upgraded from ``linux`` to ``linux | macOS`` --
  macOS port landed in #158, CI matrix in #167 onwards.
* Features section: drop the historical ``C++11/14/17/2a`` mix in
  favor of just ``C++20`` (the standard is committed; the staged
  adoption is no longer informative for a new reader).
* System Requirements section split into ``Linux`` and ``macOS``
  subsections, each listing the supported kernel/OS and the minimum
  toolchain that ships ``std::atomic<T>::wait`` / ``notify_*``:
  - Linux: GCC 11+ (libstdc++ added the operations in 11.1, per
    P1135 -- exposed locally on flare/fiber/detail/scheduling_group
    after the futex path went away in #158).
  - macOS: 13 (Ventura) or later with Apple Clang from Xcode 14+
    (its libc++ has the matching runtime symbols).
  Also calls out aarch64 / arm64 / x86_64 explicitly.
* Retranslated the zh README to read more like native Chinese -- the
  previous version had several literal-translation-isms ("吸收先前服务
  框架"... etc.). Cross-checked the en against the zh and rewrote
  several en sentences that drifted out of step over the years
  (badge alt text, "out of the box" phrasing, the third-party tarball
  blurb, etc.). Both files now describe the same project in the same
  shape.
* Added a back-link from the en README to the zh one (was one-way
  before).
* Updated the blade-build link from chen3feng/blade-build to
  blade-build/blade-build (org moved).
* Fixed bazel/support/README.md link typo (was REAMD.md).

No code changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: fix two markdown-link-check failures surfaced by the README PR

* Rename `bazel/support/REAMD.md` -> `bazel/support/README.md`.
  The file has carried the swapped-letter spelling since 2023; the
  previous README link pointed at the typo'd name, so it was working
  by accident. The README rewrite in this PR pointed at the canonical
  spelling and surfaced that the file on disk was wrong.

* Add `.github/markdown-link-check-config.json` to suppress the
  spurious "Status: 0" failure for `https://bazel.build`. The site
  returns a valid 200 to a normal browser and to `curl`, but
  apparently UA-filters the markdown-link-check action -- the action
  reads Status 0 and counts it as broken. Listed it in
  `ignorePatterns`; wired the config into the existing
  `gaurav-nelson/github-action-markdown-link-check@v1` invocation
  via the documented `config-file` input. Also bumps `retryOn429`
  (small reliability win when the action hits rate limits) and pins
  the success set to 200/206.

Both surfaced by CI on this PR; no other changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 23:21:29 +08:00
CHEN Feng d3bda1c595 thirdparty: route foreign_cc include dirs through -isystem (#171)
Two coupled changes:

1. Bump thirdparty/blade.zip from 9306687 to 5b305b0, picking up
   blade-build#1247 which adds the system_export_incs / system_include
   plumbing.

2. Switch foreign_build.bld's autotools_build and cmake_build gen_rules
   from ``export_incs=`` to ``system_export_incs=`` for the install
   include directory. Consumers now compile with
   ``-isystem<install_dir>/include`` instead of ``-I<install_dir>/include``,
   so diagnostics raised inside vendored third-party headers (glog
   self-deprecation, openssl pedantic notes, etc.) are no longer
   promoted to errors by our ``-Werror`` build.

3. Drop thirdparty/glog/silence_self_deprecation.patch + its entry in
   glog/BUILD's patches list. The patch was added in #168 to work
   around gcc-10's lack of the deprecated-inside-deprecated heuristic
   (every TU pulling in <glog/logging.h> hit the warning); the matrix
   moved to gcc-11 in #169 (where the heuristic suppresses it anyway),
   and -isystem now suppresses it generally on all toolchains.
   Two layers of belt + suspenders -> just the belt.

Verified locally on macOS: a full ``./blade build ...`` (2118 actions)
succeeds; emitted compile lines have ``-isystem<path>`` for every
thirdparty include dir and unchanged ``-I<path>`` for flare's own
export_incs.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 13:28:59 +08:00
CHEN Feng 154c68adcc thirdparty/foreign_build: source cmake's CC/CXX from the resolved toolchain (#170)
* thirdparty/foreign_build: source cmake's CC/CXX from the resolved toolchain

cmake_build hard-coded ``CC=gcc CXX=g++`` for every cmake subbuild
(glog, jsoncpp, fmt, etc.), so the thirdparty layer always invoked
the distro-default compiler regardless of what blade had picked at
config time.

This was previously masked because:

1. flare's BLADE_ROOT doesn't declare a cc_toolchain_config, so blade
   falls back to shutil.which('gcc') -- on a stock ubuntu_22 runner
   that's gcc-11 (the distro default), which happens to match what
   the cmake subbuilds were also hardcoding.
2. flare's CI workflow used to pass ``CC=gcc-10 CXX=g++-10`` as a shell
   env prefix; blade itself ignores it (no env handling), but the cmake
   subbuild *did* see it -- through the shell, not the hardcoded
   prefix. So foreign_cc and flare-side disagreed but only on a
   compiler version no longer in the matrix after #168.

After #168 + #169 the gap is benign for now (everything is gcc-11 or
clang anyway), but the hardcoded values would silently desync the
moment anyone:

- adds a cc_toolchain_config(name='ci', cc='gcc-13', ...) to BLADE_ROOT
  and runs ``./blade --cc-toolchain=ci`` -- flare libs build with
  gcc-13, glog/fmt/jsoncpp etc. build with gcc-11
- pins a non-default compiler via blade.getenv('CC', 'gcc')
- ports flare to a system where 'gcc' is not the desired compiler

Read CC/CXX from blade.cc_toolchain.tool() at config time. The proxy
returns the full path of whatever blade resolved (cc_toolchain_config
> shutil.which('gcc')). Verified locally: a forced glog rebuild now
emits ``CXX=/usr/bin/g++ CC=/usr/bin/gcc`` (the full resolved path)
instead of the bare names. Behaviour on the current CI matrix is
unchanged; the divergence is just no longer possible.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* thirdparty/foreign_build: also pass toolchain CC/CXX to autotools configure

Mirror of the previous commit for the autotools path. Without this,
configure pulls CC/CXX from $CC/$CXX in the environment (which blade
itself doesn't set) or falls back to its own autoconf detection --
both of which can disagree with whatever blade.cc_toolchain.tool()
resolved at config time. Setting CC/CXX on the configure command line
puts the thirdparty layer in lockstep with the rest of the build,
matching the cmake fix.

Verified on macOS local: a forced curl rebuild succeeds and the
emitted configure rule now contains ``CC=/usr/bin/gcc CXX=/usr/bin/g++``
(the full resolved path), rather than relying on environment leakage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 08:07:12 +08:00
CHEN Feng 802aa0b0b3 ci(test): tighten per-test timeout via --test-timeout-multiplier (#169)
* ci(test): tighten per-test timeout via blade --test-timeout-multiplier

Three coupled changes:

1. **Bump thirdparty/blade.zip** from a76de22 to 9306687, picking up
   three new blade features merged today:
   - #1241 live test output streamed to <run_dir>/blade-test.log
   - #1244 blade.getenv() for opt-in env-driven config (config phase)
   - #1246 --test-timeout-multiplier CLI flag (run-time test-timeout scaling)

2. **Lower global_config.test_timeout from 600s to 60s.** The old 600s
   was a "fits all environments" compromise that meant a hung fast unit
   test took 10+ minutes to fail (witness: dumper_integration_test
   recently stuck 60+ min on a flaky ubuntu_22 runner). Profiling on
   M2 release/-O2 shows the slowest legitimate tests today are CPU-heavy
   ones around 30-40s -- and those are all already exclusive=True so
   they don't contend with the parallel pool. 60s gives ~50% headroom
   over the slowest legit case; anything blowing past it is almost
   certainly hung and worth failing fast.

3. **Scale up via --test-timeout-multiplier=5 in CI.** GHA runners
   are slower and contended; 5x = 300s effective ceiling. Tight enough
   that a real hang fails inside the GH step timeout (~6 hours), loose
   enough to absorb routine CI variance.

Net effect: dev-machine timeouts catch hangs in 60s, CI absorbs runner
slowness, and the cap-on-slowness lives in the CI workflow (where it
belongs -- it's a property of the runner) rather than in BLADE_ROOT
(which describes the project's intrinsic test cost).

Verified locally: ./blade test ... --full-test passes all 235 tests
under the 60s baseline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(build): drop unconditional -lpthread from gtest_libs on macOS

Every cc_test that pulled gtest got '-lpthread' appended to the link
line regardless of platform. On macOS Apple's ld then warns:

  ld: warning: ignoring duplicate libraries: '-lpthread'

(pthread is part of libSystem and auto-linked; the explicit -lpthread
is redundant and triggers the duplicate-library diagnostic on the new
Xcode 16 linker).

Wrap gtest_libs in a lambda that adds '#pthread' only when targeting
Linux, matching the pattern already used for chrono / time_keeper /
thread_pool and the thirdparty glog/gflags/etc. cc_test entries.

Verified locally: every cc_test ninja file regenerated after this
change has '-lpthread' gone on macOS; nothing changes on Linux
(blade.cc_toolchain.target_os == 'linux' branch still emits it).

Note: -lrt warnings observed in CI still appear, but those come from
foreign_cc subbuilds (jemalloc / lz4 configure scripts) honoring the
host environment via autoconf detection; they're internal to those
packages, not from flare's link lines.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(build): guard unconditional '-ldl' / '-lm' with Linux check

Three places listed POSIX libraries as deps without a target_os check:

- flare/base/internal:cpu      -> '#dl'  (uses dlsym for symbol lookup)
- flare/base/internal:curl     -> '#dl'  (curl bindings)
- thirdparty/openssl:crypto    -> '#dl'  (openssl needs dlopen at runtime)
- thirdparty/protobuf-3.4.1    -> '#m'   (math symbols)

On macOS these symbols all live in libSystem and are auto-linked; the
explicit '-ldl' / '-lm' is redundant. They don't typically trigger ld's
"ignoring duplicate libraries" warning the way unconditional '#pthread'
in BLADE_ROOT.gtest_libs did (since the .tbd stubs are designed for
this redundancy), but the inconsistency was confusing and a single
"-l<x>" arriving from two places anywhere downstream would tip into a
real duplicate warning.

Wrap each in the existing `target_os == 'linux'` / `_is_linux` pattern
used elsewhere in the file. macOS link lines no longer contain
'-ldl'/'-lm'; Linux is unchanged.

Note: gperftools' BUILD still has unconditional '#pthread'/'#rt', but
gperftools is excluded on darwin entirely (the macOS port routes
through jemalloc instead), so those don't matter on macOS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 05:53:22 +08:00
CHEN Feng ed1e854280 ci: bump oldest-supported gcc to 11; make matrix.cc actually pick the matrix version (#168)
* ci: make matrix.cc actually pick the matrix gcc version

The Build and Test workflow's matrix promises gcc-10 on ubuntu_22 and
gcc-13 on ubuntu_24, but until now both jobs were actually compiling
with the distro default (gcc-11 / gcc-13) for the bulk of the build.

Cause: ``apt-get install gcc-10`` only installs the versioned binaries
(``/usr/bin/gcc-10`` etc.); ``/usr/bin/gcc`` keeps pointing at the
system default. Blade resolves the compiler via ``shutil.which('gcc')``
and reads no environment variable, so the ``CC=gcc-10 CXX=g++-10`` env
prefix on the Build step was decorative for the main build. Only
thirdparty foreign_cc subbuilds, which read $CC, actually saw gcc-10.

Effect on ubuntu_22 (the more important case -- gcc-10 is the
oldest-supported compiler the matrix claims to cover): the matrix
labelled "gcc-10" was silently testing gcc-11 for everything blade
compiled directly. Real gcc-10 compatibility was not under test.

Fix: register the matrix version with update-alternatives in Install
dependencies. After this, ``which gcc`` resolves to the matrix
version; both blade's compiler discovery and ccache's dispatch see
the right one. Drop the decorative ``CC=...`` prefixes from the Build
and Run tests steps now that they are no longer load-bearing (and
would have been misleading to future readers).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* thirdparty/glog: patch self-referencing deprecated overload for gcc-10

PR #168 switched ubuntu_22_04 from the distro-default gcc-11 (which the
prior workflow was silently using) to the actually-promised gcc-10. That
flipped the lid on a long-standing latent issue in glog 0.7.1:

  glog/logging.h:494-505 declares a [[deprecated]] type alias
  `CustomPrefixCallback` inside a `#pragma GCC diagnostic
  push/ignored "-Wdeprecated-declarations"` block, then closes the
  pragma block, then declares a [[deprecated]] overload of
  `InitGoogleLogging` whose parameter type is `CustomPrefixCallback`.

gcc 11+ has a heuristic that suppresses deprecation warnings inside a
deprecated declaration's signature, so the overload's self-reference
stays silent. gcc 10 lacks that heuristic and fires
`-Werror=deprecated-declarations` on every TU pulling in <glog/logging.h>.

Patch (silence_self_deprecation.patch) moves the overload inside the
existing pragma block. No behavior change for callers; only the
internal self-reference stops warning. macOS clang and gcc 13 are
unaffected -- the diagnostic-suppression region simply covers slightly
more code on those toolchains too.

Verified by dry-run + real apply against a fresh extraction of
glog-0.7.1.tar.gz; the patched header preserves the original semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci: bump oldest-supported gcc from 10 to 11

PR #168 made the matrix actually pick gcc-${{ matrix.cc }} instead of the
distro default (which was silently gcc-11 on ubuntu_22). With gcc-10 truly
exercised, two real incompatibilities surfaced:

1. flare/fiber/detail/scheduling_group.cc uses std::atomic<int>::wait,
   notify_one, notify_all (C++20 P1135). libstdc++ implements these
   starting in 11.1 only; gcc-10's <atomic> has no such members. PR #158
   introduced the dependency when it replaced the Linux-only futex(2)
   path with the portable C++20 primitives to make the macOS port work.

2. Several .cc files reference POSIX `unlink` without
   `#include <unistd.h>`. Newer libstdc++ stopped transitively pulling
   unistd through other headers.

Both are gcc-10 blockers we don't want to walk back -- atomic wait is
materially better than the futex path (portable, no syscall asymmetry).
Bump the matrix to gcc-11 and update the comment.

gcc-11 is the ubuntu-22.04 default, so the update-alternatives plumbing
becomes a no-op for this version but stays in place to keep future bumps
straightforward.

Keep the glog self-deprecation patch -- on gcc-11+ it is a no-op (the
heuristic suppresses the warning anyway), but it shields a local gcc-10
attempt from breaking on glog before the user even gets to the real
flare-side incompatibilities.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 00:51:16 +08:00
CHEN Feng 6df0c8d6c8 ci: use ccache to make incremental CI builds actually incremental (#167)
Background: PR #164 added actions/cache for build64_release/, but
incremental builds still run nearly cold (~15-18 min on Linux, ~14 min
on macOS) -- log shows ninja starts from [1/2956], not a partial.
Root cause: actions/checkout sets all source mtimes to "now", which
is newer than the cached .o files, so ninja decides everything has
changed and recompiles.

ccache fixes this with content-based hashing, ignoring mtime entirely.
Hooked in via the PATH-injection mechanism that ccache was designed for:

  /usr/lib/ccache/{gcc,g++,gcc-10,...}  ->  ccache binary
  echo "/usr/lib/ccache" >> $GITHUB_PATH

Blade resolves the compiler via shutil.which('gcc'), so prepending
/usr/lib/ccache to PATH redirects to the ccache shim with no change
to blade itself. ccache dispatches to the real compiler internally.

The existing build64_release cache is kept -- ccache cannot cache
thirdparty/foreign_cc subbuilds (autotools/cmake produce many compile
+ link invocations that ccache treats individually but the foreign_cc
driver itself is the slow part). The two caches are complementary:
build64_release -> thirdparty; ccache -> regular cc_library/cc_test.

Expected effect on incremental PRs (master cache hits):
  Build step on Linux: ~15 min -> ~3-5 min (only changed .cc recompile)
  Build step on macOS: ~14 min -> ~3-5 min
  Cold cache (e.g. thirdparty bump): no regression

The "ccache stats" steps print hit/miss counts so we can watch the
ratio over the first few PRs and tune if needed.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:19:14 +08:00
CHEN Feng f2da35a126 test: mark CPU-heavy tests exclusive for scheduling stability (#166)
Local profiling on M2 (release/-O2) shows these tests each consume
3+ effective cores when run standalone:

  memory_node_shared_test   811% CPU
  dpc_test                  576% CPU
  monitoring_test           554% CPU
  waitable_test             331% CPU
  hazptr_test               321% CPU

When run inside blade's parallel pool, they over-subscribe the box
and starve other tests for CPU. The primary harm isn't wall time --
it's *stability*: timing-sensitive tests neighboring these get
non-deterministic latency, leading to CI flakes that don't reproduce
locally.

Mark all five exclusive (same scheduler queue as the existing
exclusive tests: chrono_test / time_keeper_test / timer_test /
this_fiber_test / scheduling_group_test / dumper_test, which are
exclusive for the same reason -- timing accuracy under contention).

Local wall: +106s (2m44 -> 4m30) on 8-core M2 where pool is
already CPU-saturated. CI runners (2-core GHA) likely see smaller
regression -- or improvement -- because the oversubscription pain
was worse there. We'll measure on the actual CI workflow and
re-tune the threshold (currently CPU% > 300) if needed.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:19:07 +08:00
CHEN Feng 8a156b686e ci: cache bazel output + blade build64_release across runs (#164)
Both bazel and blade CI workflows currently start from scratch on every
push. Big wastes: foreign_cc thirdparty libs (curl/openssl/jemalloc/
nghttp2) each take minutes to build, and all .cc files always recompile
even when the PR only touches a few of them.

Add ``actions/cache@v4`` to both:

* ``bazel-build.yml``: cache ~/.cache/bazel (the output base; covers
  external repos, all build artifacts, and test result cache).
* ``build-and-test.yml``: cache build64_release. Key per (OS, gcc) since
  binaries aren't portable across either.

Cache key for both is hashed over WORKSPACE / BUILD files / thirdparty
source tarballs so any structural change invalidates. restore-keys
falls back to the most recent same-OS cache on miss so non-master
branches still benefit from master's cache.

Estimated effect: full cold-cache build still ~24 min; subsequent
incremental builds (most PRs) drop to ~5-10 min depending on which
.cc files changed. README/doc-only PRs become near-instant.

Caveats:
* GHA cache is capped at 10 GB/repo; older keys evicted LRU. With
  3-4 OS x compiler combinations and bazel + blade entries we may
  push that ceiling -- watch for thrash.
* PRs only read the base-branch (master) cache; cross-PR sharing
  doesn't happen.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 12:59:55 +08:00
CHEN Feng af2e463e31 test: shrink over-scaled loops in run_queue / waitable / fiber tests (#165)
* test(run_queue): shrink 3 over-scaled Torture/Overrun/Throughput cases

Local arm64 run of run_queue_test split by case:
  Basics / Steal / Nonstealable: 0 ms each
  Torture                       9'906 ms  -> 402 ms
  Overrun                      66'679 ms  -> 4'152 ms
  Throughput                    5'298 ms  -> 327 ms
  total                        81'883 ms  -> 5'448 ms (15x)

All three were stress tests with iteration counts well beyond what
the assertions need:

* Torture: N=1M items / T=200 threads / k=10 outer  =>  N=200K / T=100 / k=3
  (still 60M operations -- plenty to exercise the ring buffer)
* Overrun: N=100K / T=40 / k=10  =>  N=20K / T=20 / k=3
  (still hammers the queue's overrun-handling path repeatedly)
* Throughput: N=1M / T=200 / k=10  =>  N=200K / T=100 / k=3
  (still measures real throughput)

Same pattern as #163: keep the test's intent, drop the over-budget loop
sizes. Largest single-test win identified after that PR landed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(waitable, fiber): shrink more over-scaled loops

After run_queue_test (above commit), next-biggest single-test offenders
on local arm64 (release/-O2) were:

  waitable_test     43.7s
  fiber_test        13.9s

waitable_test (24 parameterized cases):
  ConditionVariable           N=10000, outer=10  ->  N=2000,  outer=3
  ConditionVariable2          N=1000,  outer=50  ->  N=1000,  outer=10
  ExitBarrier                 N=10000, outer=10  ->  N=2000,  outer=3
  Event                       N=10000, outer=10  ->  N=2000,  outer=3
  OneshotTimedEventTorture    N=10000, outer=10  ->  N=2000,  outer=3
                              (rest leave alone -- already small)
  Result: 43.7s -> 24.1s

fiber_test::SchedulingGroupLocal had a hardcoded
``while (start + 10s > ...)`` busy-wait. Comment said "10s should be
far from enough" (intentionally generous; the assertion under test
fires immediately if the bug is present). Reducing to 3s preserves
both intent and the assertion's safety margin.
  Result: 13.9s -> 7.5s

Combined wall savings on flare's CI: ~30s after 10-way test parallelism.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 11:20:38 +08:00
CHEN Feng d5e44603db test: shrink over-scaled loops + exclude *_benchmark from bazel CI
Six commits' worth of test-step CI wall-time reduction.

* Loop reductions in five tests that were stress-testing far harder than
  their assertions required. Local arm64 fastbuild measurements:
    semaphore_test            115.6s -> 1.1s  (105x; cut 1M threads to 10K)
    id_alloc_test              50.7s -> 0.9s  (56x;  cut 1B allocs to 10M)
    hash_map_test              42.0s -> 1.8s  (23x;  cut 5M iter to 500K)
    timer_worker_test          31.0s -> 5.8s  (5x;   cut 4M ops to 100K)
    writing_buffer_list_test   22.0s -> 4.5s  (5x;   cut Torture configs)

* Tag *_benchmark cc_test with tags = ["benchmark"] and exclude from CI
  via --test_tag_filters=-benchmark. 30 BUILD.bazel targets tagged.
  Bazel CI was running 30 benchmark cc_test targets including
  hash_map_benchmark (86s), case_insensitive_hash_map_benchmark (70s),
  dpc_benchmark (54s). Matches blade's semantics where cc_benchmark is
  a cc_binary (not picked up by ./blade test).

Estimated CI wall savings ~3 min on bazel job. All reduced tests still
exercise the same race / overflow / stress patterns -- just at sensible
iteration counts.
2026-06-03 09:00:20 +08:00
CHEN Feng 51cf5a5587 ci: unflake timing tests + bump deprecated actions + blade.zip (3 fixes)
Three CI hygiene fixes bundled in one PR.

* **Flaky timing assertions widened**. Two tests had ASSERT_NEAR / EXPECT_NEAR
  with budgets too tight for shared-VM CI runners:
  - flare/net/http/binlog_dry_runner_integration_test:220 -- 20ms -> 50ms
    (observed 136ms vs expected 100ms on GHA).
  - flare/io/native/stream_connection_test:411 and :491 (WriteBandwidthLimit
    / ReadBandwidthLimit) -- 1s -> 2s (observed 8.4s vs expected 7s).
  Both tests still validate order-of-magnitude correctness; just give
  shared-runner jitter the headroom it needs.

* **Deprecated GitHub Actions bumped** to silence the "Node.js 20 actions
  are deprecated" warning (forced upgrade scheduled for Sept 2026):
  - actions/checkout: @v2 / @v3 / @master -> @v6 (Node 24)
  - actions/setup-python: @v2 -> @v6

* **thirdparty/blade: bump blade.zip to master a76de22**. Picks up three
  upstream blade-build fixes that surfaced after master a8d12b2 wired in
  the new cc_check_undefined static check:
  - #1238: add __tls_get_addr / __tls_get_offset to undefined-check
    baseline (ld-linux.so injects these); unbreak stale CheckUndefinedDiff
    unit tests; defer PrebuiltCcLibrary._emit_archive_syms to generate
    phase (was tripping the __build_code-None invariant in
    Target.before_generate).
  - #1239: treat uppercase 'V' / 'W' nm symbol types as defined, not
    ambient -- they're weak DEFINED, not weak UNDEFINED.
  - #1240: treat lowercase 'u' as defined (GNU "unique global"
    extension) -- fmt's basic_data<void>::*_padding_shifts and ~40 other
    template-static-data symbols were silently dropped from libfmt.a's
    syms set, breaking any cc_library that transitively used fmt.
2026-06-03 07:19:42 +08:00
CHEN Feng 8cc6c17c63 thirdparty/blade: bump blade.zip to current master (a8d12b2) (#161)
The vendored blade in `thirdparty/blade/blade.zip` was at commit
82ba580 (the export_map → -exported_symbols_list change), which
predates the dep-check improvements merged later the same day:

  82ba580  ← bundled here previously
  9a0138e  Cache ToolChain.get_cc_target_arch (#1224)
  6db819c  incchk: cut false positives in unused-deps + docs (#1231)
  7f182fa  feat(cc): decouple undefined-symbol checking (#1225) (#1233)
  e4e0c8b  docs(cc): document static undefined-symbol check (#1234)
  a8d12b2  docs(cc): two more missing-dep cases + log demote (#1237)  ← now bundled

User-visible change: `./blade build flare/...` now reports the same
dependency notices that the upstream master blade does. With the
older bundle, a full `flare/...` build surfaced 62 Unused-dep notices
that the four auto-exemption rules introduced in #1231 already
handle (header re-export across umbrella + sub-target, export_incs-
relative paths, foreign_cc_library header-less auto-exempt, no-
scannable-source skip). Re-bumping clears them without any BUILD
edits.

Numbers on macOS arm64, -j10, full `./blade build flare/...`:

  Notice category   Before bump   After bump
  ----------------  -----------   ----------
  Missing-dep                0            0
  Unused-dep                62            0
  Build success                yes (no functional change)
2026-06-02 08:30:25 +08:00
CHEN Feng 6a159d839b Resolve the two long-standing Missing-dependency notices (#160)
`blade build --cc-check-undefined flare/...` has been emitting two
Missing-dep notices since PR #159 went in -- they were known and
intentionally left at the time as "visibility-blocked" cases. With a
follow-up look they turn out to be two distinct patterns, each fixable
without exposing internals:

* `flare/base:exposed_var_test` -- `flare/base/write_mostly/metrics.h`
  was a dead include. The test only uses `ExposedMetrics<T>` (defined
  in `flare/base/exposed_var.h`, already included); it never names
  any symbol from `metrics.h`. blade was correctly reporting the
  include; the right fix is to delete the include, not add a dep on
  `:write_mostly` (private encapsulation preserved).

* `flare/rpc:rpc` -- `flare/rpc/http_filter.h` / `http_handler.h`
  (rpc:rpc's own public hdrs) re-`#include` `protocol/http/http_filter.h`
  / `http_handler.h` so consumers of the outer facade see the types.
  `:http_filter` / `:http_handler` had `visibility = '//flare/rpc:http'`,
  which blocked rpc:rpc from depending on them even though it's
  exactly the canonical re-exporter. Widen the visibility narrowly to
  also allow `//flare/rpc:rpc` (C++ `friend`-style allow-list -- still
  not PUBLIC, still locked down to the two known facade targets), then
  declare the deps explicitly on the umbrella's side. Both endpoints
  carry comments explaining the re-export relationship.

After this: `blade build --cc-check-undefined flare/...` reports zero
Missing-dep notices and zero Unused-dep notices.

For the design rationale of both patterns, see the corresponding
blade-build docs PR which adds them as cases 6 and 7 in
"Fix missing dependencies errors caused by `hdrs`".
2026-06-02 07:45:37 +08:00