github.com-facebook-folly
all · 56 devs · built 2026-06-13
Repository snapshot
Monthly reports
Highlights
- Extensive modernization of *Folly* components by replacing SFINAE with **C++20 concepts** for template constraints across modules like *folly/lang/CheckedMath*, *folly/Poly*, *folly::Optional*, *folly::HazptrHolder*, *folly::ThreadLocal*, and *folly/coro/SharedPromise.h* (e.g., [1d038185 · Richard Barnes], [768d25ce · Richard Barnes], [4ed52d39 · generatedunixname1526759334626947]).
- Enhanced *IoUring* capabilities with support for `IORING_RECVSEND_BUNDLE` to reduce completion overhead ([17df2538 · Clément Léger]) and the addition of ZCRX kernel notifications and statistics for improved monitoring ([548c7fb0 · Clément Léger]).
- Introduced a new `with()` method to *folly/observer* classes, providing a safer, scoped mechanism for accessing observed values and preventing read-after-free issues ([f8bf14bd · Dong Shi], [4f257c7e · Dong Shi]).
- Critical bug fixes in *Folly's JSON float serialization* to ensure correct scientific notation output and prevent invalid JSON ([d108ff74 · Richard Barnes]), alongside the introduction of `FloatFormat::SHORTEST_SINGLE` for accurate `float` precision ([789c2a64 · Richard Barnes], [4375d40c · Richard Barnes], [ae684167 · Richard Barnes]).
- Resolved a critical bug in `getdeps` GitHub Actions workflow generation by removing an invalid `--recursive` flag, ensuring successful CI builds ([3d5dd8e1 · Richard Barnes]).
Observations
- Overall development output decreased by 40% compared to the 2-month average (7 current vs 11 average), reflecting a lower volume of new features and maintenance work.
- Commit volume significantly decreased by 53% this month (145 commits) compared to the 2-month average of 308 commits, indicating a substantial slowdown in activity.
- Maintenance activity saw a notable decrease of 38% compared to the 2-month average (4 current vs 6 average), aligning with the overall reduction in commit volume.
- Waste score dramatically dropped by 77% this month (1 current vs 3 average) compared to the 2-month average, suggesting a period of highly efficient development with minimal rework or reverted changes relative to the output.
- The `FloatFormat::SHORTEST_SINGLE` feature in *folly::json* experienced significant churn, being introduced ([789c2a64 · Richard Barnes], [4375d40c · Richard Barnes]), then reverted ([55501925 · Tuan Pham]), and subsequently re-added as a regression fix ([ae684167 · Richard Barnes]), indicating rework in this area.
- The `-Wunsafe-buffer-usage` fix was subject to rework, with an initial fix being reverted ([c8a7db0f · Richard Barnes]) before a safer C++ abstraction-based solution was re-implemented ([a5cd653e · Richard Barnes]).
- The `dirsync` redirect mechanism in the *folly build system* also showed rework, with its enablement being reverted ([7187dee4 · Daniel Bradleigh Crockett]) and then re-enabled within the same period ([6eaea0b9 · Daniel Bradleigh Crockett]).
- A consistent pattern of modernization was observed with 11 commits dedicated to replacing SFINAE with **C++20 concepts** across various *Folly* components, improving code readability and maintainability.
- The `getdeps` build system continued to be a focus for improvements and fixes, including refactoring for GitHub Actions workflow generation ([19934f18 · Richard Barnes], [020b9bfc · Richard Barnes]) and a critical bug fix for build failures ([3d5dd8e1 · Richard Barnes]).
Performance over time
ETV stacked by Growth, Maintenance and Fixes — 90-day moving average, normalized to ETV / month.
Average performance per developer
ETV per active developer per month — 30-day moving average.
Active developers over time
Unique developers committing each day — 90-day moving average.
Knowledge concentration
How dependent is this repo on a small number of contributors? Higher top-1 share = higher key-person risk.
Yedidya Feldblum owns 26.8 % of commits.
Top contributors
Most impactful commits
Top 20 by ETV in the all-time window.
- 2.1ETVcli_apply_args_files Summary: In the context of CLI args-parsing, a facility to apply args-files to an args vector. An arg beginning with `@` but not with `@@` is treated as a filename, relative to the current directory if relative, after the `@`. That arg is then replaced by parsing the file into a sequence of arguments, recursively. Reviewed By: hchokshi Differential Revision: D92167066 fbshipit-source-id: 9652db02b04202fa258f6589d468568d5f51d3d5Yedidya Feldblum · 56a01e21 · 2026-02-10
- 1.7ETVAsyncSocketTest: parametrize with IoUringBackend Summary: Validate native AsyncSocket support with IoUringBackend. Parametrize most tests (that make sense) with the backend, testing both the standard libevent backend and the io_uring backend. Reviewed By: vishwanath1306 Differential Revision: D90996725 fbshipit-source-id: a60b1702f1b6efe8d089ef1f2cb8e5b2d8207f42David Wei · 193b53f3 · 2026-01-21
- 1.6ETV`AsyncClosure.h` implement `async_closure` with async RAII Summary: Here's the short rationale for `async_closure()`. The header has a user-facing tl;dr, and `docs/` provide far more context. - It provides a robust solution to the "async cleanup" / "async RAII" problem. `co_cleanup_capture` allows the closure to await multiple cleanup steps on exit (like `co_scope_exit`), but the cleanup is actually enforced to be memory-safe (unlike `co_scope_exit`). - Async scopes owned by a closure can support "natural" cancellation, without incurring additional cost for it the way you would with `CancellableAsyncScope`. - It supports compile-time-checked "safe" reference-passing from ancestors to descendants. From a user's perspective, they just pass `capture`s into child closures, and it works. Or when it fails to compile, this means there may be a memory-safety bug. --- The integration of `MemberTask` with `async_closure` is a bit magic, in two ways: - A special branch lets us use the pre-existing `FOLLY_INVOKE_MEMBER` "overload set" convention with `capture` and `AsyncObjectPtr` wrappers that require dereferencing. We could avoid this by introducing a separate macro like `FOLLY_INVOKE_MEMBER_INDIRECT` or `FOLLY_INVOKE_MEMBER_TASK`, but the current UX feels comfortable, and I don't see a big risk. - (true as of the prior diff) As with `ClosureTask`, `async_closure` implicitly re-wraps `MemberTask` making it movable and upgrading its `SafeTask` safety level. PS We should eventually have a linter that enforces that `MemberTask` is only used for non-static member functions, but this seems low-risk for now. *The user-facing explanation of this is in `APIBestPractices.md` (D65408249).* Reviewed By: ispeters Differential Revision: D63299858 fbshipit-source-id: a3519b3873c7b0e9859db9f848a4cde916a950bfAlexey Spiridonov · 9b095871 · 2025-03-18
- 1.5ETValgorithms for byte-sized find_first_of, find_first_not_of Summary: Includes a selection of scalar and vector algorithms all implementing `find_first_of` and `find_first_not_of`. May be useful to accelerate select parsers. Reviewed By: DenisYaroshevskiy Differential Revision: D61775260 fbshipit-source-id: 32fdd8e7c02c66a54764b0ae847aec1fa9564678Yedidya Feldblum · 8e49a654 · 2025-03-18
- 1.5ETVresult<T>: value-or-exception_wrapper type & short-circuiting coro Summary: ## Pitch for "working programmers" `result<T>` is a more ergonomic, safer `Try<T>` replacement for code that, for reasons of performance or reliability, chooses to explicitly propagate errors via `return`. Consider this "idiomatic `Try` code": ```cpp // Bug farm: what if this accidentally throws? Try<int> mayFail() { ... } Try<float> alsoFallible() { Try<int> tryN = mayFail(); float v = 5.0; if (tryN.hasValue()) { v += *tryN; } else if (tryN.hasError()) { return Try<float>(tryN.error()); } else { // This bug is easy to make, since `Try` default-constructs as empty! LOG(DFATAL) << "BUG: `mayFail()` returned an empty Try"; } return Try<float>(v); } ``` With `result`, both potential bugs are prevented, and the code is *just* business logic: - "What if `mayFail()` accidentally throws?" is a non-issue since `alsoFallible()` is a `result` coroutine, and thus automatically wraps any uncaught exceptions. - "Empty `Try`" bugs go away since `result` is almost-never-empty [*] ```cpp result<int> mayFail() { ... } result<float> alsoFallible() { co_return 5.0 + co_await mayFail(); } ``` [*] Until we have `std::expected` in C++23, `result<T>` is implemented via `folly::Expected<T, exception_wrapper>`, which is merely "almost-never-empty", rather than "never empty". That said, you will be hard-pressed to hit the empty state with well-formed types -- I tried and failed to construct one naturally in `result_test.cpp` (and settled for "synthetic" objects in an empty state). If you **do** manage hit the empty state, the `result` accessors are carefully crafted to degrade gracefully, so the end user really doesn't have to test for it. --- ## Cancellation -- library-author considerations Looking ahead to C++26 and [P2300](https://wg21.link/p2300), note that receivers have 3 completion states: value/error/stopped. In `folly::coro`, the latter 2 are fused -- cancelled tasks finish with exception `OperationCancelled`. On the other hand, [P2300](https://wg21.link/p2300) takes pains to separate them. That thinking derives from [P1677](https://wg21.link/p1677). The central "working programmer" problem that "cancellation is not an exception" tries to address is, in paraphrase: > "Child stopped" should NOT be handled by exception catch-alls, since a higher-level orchestrator decided the work is no longer needed. The right behavior is to free the resources from this tree of execution as quickly as possible. So, by letting "stopped" state bypass regular exception-handling, [P1677](https://wg21.link/p1677) / [P2300](https://wg21.link/p2300) aim to prevent the class of bugs where cancellation gets accidentally caught and "handled". Today's users of `folly` expect only 2 states: value & error (possibly `OperationCancelled`). Since `result` is a new API, this stack takes the opportunity to add some forward-compatibility. First, D72181807 and D72074521 take some steps to discourage the use of raw `OperationCancelled` by end-users -- `result` is one of the suggested solutions. Second, the top-level `result<T>` API is sort-of bi-state -- it exposes only `has_value()` as a primary test. The "error" and "stopped" state are deliberately hidden in a `non_value()` sub-structure. Yes, `result` also has `has_stopped()`, but that's just shorthand for `!has_value() && non_value().has_stopped()`. This two-level design works quite well to reconcile the goals of "good UX today" and "forward-compatibility with value/error/stopped semantics": - Getting values is easy & safe -- `co_await ...` / `co_await coro::co_ready(...)`. These recommended idioms auto-propagate unhandled errors & cancellation. - Testing specific errors is easy & safe -- `get_exception<Ex>(res)`. - Manually propagating unhandled errors & cancellation is also easy & safe -- `return std::move(res).non_value()`. On the other hand, `result` do **not** provide an easy catch-all -- it would be prone to accidentally catching the cancellation exception! Rather, `result` **deliberately** omits `has_error()` or `error()` -- you have to access `non_value()` to get at those, meaning that you're more likely to correctly test `has_stopped()`. From a user perspective, `result` is `variant<T, variant<stopped, error>>`. However, the implementation is just `Expected<T, exception_wrapper>` -- and it efficiently ingests exceptions from `folly::coro` and `Try`, which don't treat "stopped" separately. Here's how that works: - We use `make_legacy_...` and `get_legacy_...` methods to interact with old-school implementations like `folly::coro` & `Try`. These methods expect an `expection_wrapper` which **may** be an `OperationCancelled`. - In contrast, using normal **end-user** APIs, debug builds will **terminate** if you store `OperationCancelled` in, or retrieve an `error()` from a stopped-state `result` / `non_value_result`. The error says to use `has_stopped()` and `stopped_result` instead. Finally, `result` also prohibits `error()` from being an empty `exception_wrapper` -- that unconditionally terminates if you try to re-throw it! This creates some option value -- once we actively go after `OperationCancelled` deprecation, we can potentially co-opt this empty state as a cheap cancellation signal. Reviewed By: ispeters Differential Revision: D71522263 fbshipit-source-id: 8ae98c979c61e5cd9a26dfc26fbf86dd36471b3fAlexey Spiridonov · 1a8c80c7 · 2025-04-04
- 1.5ETVAdd --bm_mode=adaptive; change default --bm_min_usec=1000 Summary: # `--bm_mode=adaptive` I got frustrated with manually retrying benchmark runs to avoid system performance oscillations, and with manually aggregating these runs to get a reasonably precise number. Existing modes (regular or `--bm_estimate_time`) weren't working very well for me -- too noisy, too slow, or both. The new `--bm_mode=adaptive` tries to automate a "statistically sound measurement" for a system that is not short-term stationary. Think of it is "automatic noise cancellation" for the modern reality of working on multi-tenant VMs. If you're used to doing best-of-5 -- this is the same idea, but better. Read `docs/BenchmarkAdaptive.md` for a proper description, and the caveats. --- # `--bm_min_usec` default change: 100μs -> 1ms **This makes default runs 3-8x slower, but they'll stop being wrong.** Here are 5 back-to-back runs with `--bm_min_usec=100 -bm_max_secs=30` on a quiet system. I raise "max_secs" because the default under-samples with larger `min_usec`, giving even more noise. ``` LegacyCaseInsensitiveCheck 9.23us 108.33K CurrentCaseInsensitiveCheck 1.41us 711.35K LegacyCaseInsensitiveCheck 9.23us 108.33K CurrentCaseInsensitiveCheck 1.54us 650.59K LegacyCaseInsensitiveCheck 9.23us 108.33K CurrentCaseInsensitiveCheck 1.40us 712.26K LegacyCaseInsensitiveCheck 8.41us 118.87K CurrentCaseInsensitiveCheck 1.54us 650.26K LegacyCaseInsensitiveCheck 9.23us 108.33K CurrentCaseInsensitiveCheck 1.41us 710.83K ``` Wildly inconsistent! Each took 1.6 seconds, so 8 seconds total, and we're none the wiser about the true distribution. And it's much slower than the typical 1-2 sec for `adaptive` on the same 2 benchmarks, which *does* produce consistent results, fast. ``` $ time buck run @//mode/opt fbcode//folly/test:ascii_case_insensitive_benchmark -- --bm_mode=adaptive ... LegacyCaseInsensitiveCheck 8.54us 117.09K CurrentCaseInsensitiveCheck 1.40us 714.48K real 0m0.870s ``` With 100μs slices above, the regular runs shows cache interference from the benchmark harness code (I'm 80% sure that's the cause, from my experiments). Going to `--bm_min_usec=1000` hides that, and makes regular runs consistent too. With 1ms slices, regular-mode runs look like this, but they now take **~12 seconds** each (only 4 with default `max_secs`, but that's noisier). But hey, at least you get usable data! ``` LegacyCaseInsensitiveCheck 8.50us 117.60K CurrentCaseInsensitiveCheck 1.39us 718.93K LegacyCaseInsensitiveCheck 8.50us 117.61K CurrentCaseInsensitiveCheck 1.42us 704.37K ``` The timings differ slightly, since `adaptive` measures p33 by default, while regular always measures p0. If 1ms is deemed "too slow by default", 500μs is borderline -- you still see unacceptable interference, but not as much. I could imagine landing with that for the regular/legacy mode, and giving `adaptive` a better default. It'd be cool to redesign of the benchmark setup to skip timing the first few iterations of each slice to mitigate this more generally, but I'm not sure the cost-benefit is favorable. Reviewed By: yfeldblum Differential Revision: D92348138 fbshipit-source-id: 92dda73d6753a07aeac9b51cb4efb155cd1f6143Alexey Spiridonov · 2108e961 · 2026-02-25
- 1.5ETVReproducible floating-point summations Summary: Introduce ReproducibleFloatingAccumulator. Header-only C++ library for reproducible (order-independent) floating-point summation using binned floating-point arithmetic, adapted from ReproBLAS v2.1.0. Key features of the accumulator: - Reproducible summation independent of summation order - Accuracy at least as good as conventional summation, and tunable via FOLD - Handles overflow, underflow, NaN, and infinity reproducibly - Single read-only pass over summands; minimal memory (2*FOLD floats - usually 6 since FOLD=3 is sufficient for most purposes) Public interface: - Default, copy, and implicit scalar construction - operator+=/-= for scalars and accumulators - Binary operator+/- (including scalar-accumulator mixed expressions) - operator== for bitwise equality - Explicit conversion to native float via operator ftype() and value() - Batch add() via iterator pairs, C++20 ranges, or single values - Manual unsafe_add/renorm path for performance-critical loops - Error bound computation - fmt::formatter specialization (always available via fmt/format.h) - static_assert(FOLD >= 2) to catch invalid instantiation # Example ``` ReproducibleAccumulator<double> rfa; for (const auto& x : kDoubleData) { rfa += x; } rfa.value(); // Same result irrespective of ordering of kDoubleData ``` # Benchmark ``` ============================================================================ [...]/ReproducibleAccumulatorBenchmark.cpp relative time/iter iters/s ============================================================================ SumDouble_OneAtATime_10 50.69ns 19.73M SumDouble_BatchAdd_10 55.744% 90.93ns 11.00M ---------------------------------------------------------------------------- SumDouble_OneAtATime_1000 4.11us 243.32K SumDouble_BatchAdd_1000 132.29% 3.11us 321.88K ---------------------------------------------------------------------------- SumDouble_OneAtATime_1M 4.11ms 243.12 SumDouble_BatchAdd_1M 137.09% 3.00ms 333.30 ---------------------------------------------------------------------------- SumFloat_OneAtATime_1M 4.14ms 241.69 SumFloat_BatchAdd_1M 137.35% 3.01ms 331.96 ---------------------------------------------------------------------------- SumDouble_NaiveBaseline_1M 1.03ms 975.29 SumFloat_NaiveBaseline_1M 1.02ms 975.89 ---------------------------------------------------------------------------- SumDouble_Kahan_1M 13.31ms 75.11 SumFloat_Kahan_1M 13.32ms 75.10 ---------------------------------------------------------------------------- SumDouble_LongDouble_1M 2.39ms 418.41 SumFloat_LongDouble_1M 2.39ms 418.72 ``` Reviewed By: yfeldblum Differential Revision: D97780214 fbshipit-source-id: 07399b5f905f3a021252560ad80df47fe53db21aRichard Barnes · 5ae1dd9b · 2026-03-29
- 1.4ETVcstring_view, operator""_csv Summary: A class like `string_view`, but representing a pair of a C string and its precalculated size. Similar to [p3655r3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3655r3.html). Reviewed By: ilvokhin Differential Revision: D84381600 fbshipit-source-id: c793679ea6ebd95f2db80b8afd182384af476d36Yedidya Feldblum · 15947ac4 · 2025-10-15
- 1.2ETVAdd folly::kahan_sum and folly::accurate_sum to folly/math Summary: Extract compensated summation algorithms into reusable libraries. KahanSummation.h uses the Neumaier improvement over Kahan's original algorithm, which handles the case where the new summand is larger in magnitude than the running sum. Despite the branch, Neumaier runs ~1.85x faster than Kahan's original on modern out-of-order CPUs due to a shorter loop-carried dependency chain. The library is named "Kahan" because Kahan-Babushka-Neumaier is the standard family name and most callers don't need to know the variant. New headers: - KahanSummation.h: kahan_accumulator<T> and kahan_sum() free functions using C++20 concepts/ranges. Uses compiler_must_not_predict to prevent the compiler from defeating the compensation mechanism. Implements the Neumaier variant internally. - AccurateSummation.h: accurate_sum() that dispatches to wider-type accumulation (float→double, double→long double) when the element count is below a threshold derived from error analysis, falling back to Kahan/Neumaier summation for larger inputs. Guarded by std::numeric_limits::digits comparisons for portability. Also renames the benchmark to SummationBenchmark covering all algorithms, simplifies ReproducibleAccumulatorTest accuracy checks to compare only against a long double Kahan truth, and adds dedicated test suites for both new libraries. # Benchmarks ``` ============================================================================ [...]olly/math/test/SummationBenchmark.cpp relative time/iter iters/s ============================================================================ SumDouble_Naive_1M 1.03ms 975.25 SumFloat_Naive_1M 1.03ms 975.52 ---------------------------------------------------------------------------- SumDouble_KahanAccumulator_1M 3.34ms 299.15 SumDouble_KahanSum_1M 100.08% 3.34ms 299.38 ---------------------------------------------------------------------------- SumFloat_KahanAccumulator_1M 3.34ms 299.29 SumFloat_KahanSum_1M 100.03% 3.34ms 299.38 ---------------------------------------------------------------------------- SumDouble_AccurateSum_1M 3.34ms 299.18 SumFloat_AccurateSum_1M 1.02ms 975.62 ---------------------------------------------------------------------------- SumDouble_LongDouble_1M 2.39ms 418.40 SumFloat_LongDouble_1M 2.39ms 418.55 ---------------------------------------------------------------------------- SumDouble_ReproducibleOneAtATime_10 50.93ns 19.63M SumDouble_ReproducibleBatchAdd_10 56.132% 90.74ns 11.02M ---------------------------------------------------------------------------- SumDouble_ReproducibleOneAtATime_1000 4.11us 243.30K SumDouble_ReproducibleBatchAdd_1000 131.76% 3.12us 320.56K ---------------------------------------------------------------------------- SumDouble_ReproducibleOneAtATime_1M 4.11ms 243.12 SumDouble_ReproducibleBatchAdd_1M 136.72% 3.01ms 332.40 ---------------------------------------------------------------------------- SumFloat_ReproducibleOneAtATime_1M 4.14ms 241.60 SumFloat_ReproducibleBatchAdd_1M 137.30% 3.01ms 331.71 ``` Reviewed By: skrueger Differential Revision: D98649429 fbshipit-source-id: 6933a12531553d0b31ee8f069f6976a512f69f8fRichard Barnes · 9df0b849 · 2026-04-05
- 1.0ETV`Captures.h` Summary: The full docs are in `Captures.md` in a stacked diff. Here's a summary. `*capture*` types from this small zoo are created by `async_closure()` users via the `as_capture()` binding modifier. The details of the types don't matter for typical usage, since the user should just write `auto paramName` coro declaration for their closure, and treat it as a pointer type. `capture<V>` is a value wrapper, while `capture<V&>` and `capture<V&&>` are reference wrappers. - `capture<T>` is meant to quack largely like `T` - The wrappers aim to be transparent to value-category qualifiers. For example `const capture<V&>` -> `capture<const V&>`; `capture<V>&& -> capture<V&&>`, etc. - All `capture`s expose a uniform pointer-like syntax, but none are nullable (except `capture_indirect`, which is opt-in). I'm settling for `->` and `*` because C++ doesn't really allow for "transparent" reference wrappers. ## Why are `capture`s implicitly convertible to refs? The docblock above `operator ref_like_t<int&>()` (and friends) goes into great detail about why these implicit conversions are a good idea. The motivation in short is -- without such implicit conversions, the "elide outer coro" optimization of `async_closure()` becomes much more onerous, because closure users have to worry about whether they have an outer coro or not. With the implicit conversion, closure inner coros can take the correct `capture` ref wrapper, and have their code work in both scenarios. I should add that the "elide outer coro" optimization overall does improve the UX, since it lets us pass `as_async_closure()` methods into `schedule*Closure`, and lets people use `async_closure` whenever they want `capture` ref wrappers, and not just when they need async RAII. ## What's with the `capture_proxy()` ADL customization point? Defining the friend function `capture_proxy()` allows user classes to provide custom return objects for `capture`'s operators `*` and `->`. This motivating example for this is the `SafeAsyncScope` implementation. The detailed reasons are described in the docblock above `get_lref_wrapper()`. Reviewed By: ispeters Differential Revision: D63299819 fbshipit-source-id: 795af30ce67afd554d8b773213ba61933c6c5625Alexey Spiridonov · 51054155 · 2025-03-18
- 1.0ETVTests sit in `folly::test` namespace Summary: The point here is just to avoid accidental collisions between test & prod identifiers. There are a couple of exceptions due to friend-tests, which I did **not** want to have to forward-declare... Reviewed By: janondrusek Differential Revision: D91987133 fbshipit-source-id: 7b40409cde1975b45ca63ed3feae5d78ea67c04fAlexey Spiridonov · 0de729c0 · 2026-02-04
- 1.0ETVstack_epitaph captures call stack instead of source location Summary: `stack_epitaph()` captures the call stack and attaches it to `result` / `error_or_stopped` errors, with an optional user message. ## Motivation When a `result` coroutine's `unhandled_exception()` fires, the error says *what* was thrown but not *where*. This diff makes `unhandled_exception()` automatically attach a stack-trace epitaph: ``` std::runtime_error: boom [via] [stack: #0 coroThatThrows() @ stack_epitaph_test.cpp:70 #1 ...] ``` **Limitation**: The stack is captured after `throw` unwinds to the coroutine frame — you see which coroutine threw and its callers, but not the leaf that called `throw`. ## API `stack_epitaph()` wraps an error-or-stopped with a stack trace. Same semantics as `epitaph()` — transparent to `get_exception<T>()` and error codes, discarded on throw/eptr conversion, values pass through. ```cpp // Automatic in result coroutines (via unhandled_exception). // Public for user catch clauses: co_return stack_epitaph( error_or_stopped::from_current_exception(), "context"); ``` ## Costs - CPU: ~10ns/frame (libunwind walk). At ~20 frames, ~200ns — under 10% of throw+catch cost (~2us). - Memory: 192 bytes inline per epitaph. ~2KB transient stack for the capture buffer. ## Design `epitaph_non_value` is generalized into `epitaph_impl<Location>`, parameterized on location storage. Regular epitaphs use `epitaph_source_location` (zero-size via `[[no_unique_address]]`); stack epitaphs use `epitaph_stack_location<Opts>`. All underlying-pointer lifetime logic exists once. Stack frames use split storage: 17 frames inline (192 bytes = 3 cache lines), overflow in a ref-counted `shared_ptr<uintptr_t[]>`. When `inline_frames >= max_frames`, the heap member compiles to zero bytes. Capture uses `getStackTrace()` (libunwind); symbolization is deferred to format time. Reviewed By: janondrusek Differential Revision: D92859914 fbshipit-source-id: 51762e18b162105a4c424df633eaf2f81227e5a0Alexey Spiridonov · 9fbb6434 · 2026-02-27
- 0.9ETVFold most `NowTask` logic into `TaskWrapper` + fixes Summary: This improves `TaskWrapper` and makes `NowTask` nearly trivial. Up-stack, this also removes a bunch of duplicative protocol-forwarding code from `SafeTask`. Main refinements: - Much better doc coverage for `TaskWrapper`. - Add object slicing detection on methods that explicitly unwrap. - Add `promise_type` to `OpaqueTaskWrapperCrtp` since it seems desirable for it to always be a coroutine type. - Use configuration structs to make the setup of the CRTP bases cleaner and more flexible. - Provide `TaskWithExecutorWrapperCrtp` and a `TaskWithExecutorT` config so that `TaskWrapperCrtp` can reference it. - Stop forwarding unsafe APIs like `scheduleOn` and `semi`. - Rename some identifiers for brevity / readability. In particular, what used to be `protected` `unwrap()` became `unwrapTask()` and `unwrapTaskWithExecutor()` to avoid conflicting with the awaitable wrapper from `ViaIfAsync.h` and with the other `unwrap()` from `Task.h`. - Expand test coverage. Reviewed By: ispeters Differential Revision: D70805317 fbshipit-source-id: 19c7ee55ca1302b4094fe6ce1eff15cc2b56dc3eAlexey Spiridonov · efbf05d6 · 2025-03-08
- 0.9ETVMove CommandLineParser from common/settings to folly/settings Summary: * These files are responsible for parsing folly settings from command line arguments * They're currently part of common but I think make sense to colocate with the folly settings library itself (like how gflags exposes `gflags::ParseCommandLineFlags`) * No code changes except for the namespaces (`facebook::settings` -> `folly::settings`) * This could eventually unblock adding an option to parse folly settings from folly/Init Reviewed By: Gownta Differential Revision: D69604867 Privacy Context Container: L1259632 fbshipit-source-id: 512ee5920c24deb9f9eac3c0a56b7b007dcdcf75Kevin Doherty · e43ee69a · 2025-03-06
- 0.9ETVAwait in all supported coros via `or_unwind` Summary: The intent is to remove the old `co_await` APIs for "get `result` value or propagate error/stopped", and replace it with a uniform, and more capable `co_await or_unwind(...)`. This addresses a few deficiencies of the old API: - `co_await resFn()` in `result` coros may be confusable with awaiting async `folly::coro` tasks. - Without the `or_unwind` verb, there is no obvious breadcrumb for a result-coro novice to look up docs. - One had to remember a separate `co_await co_ready(resultFn())` incantations to unpack results in `folly::coro` coroutines - In the single-minded pursuit of preventing accidental copies, I required `co_await std::ref(res)` syntax to access the innards by lvalue ref. Furthermore, this API wasn't (yet) available with `co_ready`. With `co_await or_unwind(res)` just works, as in regular idiomatic C++. The last bullet does come with a higher chance of accidental copies, but it's the right tradeoff. The technical summary of the changes is: (1) * Before the refactor, you could write write `co_await resultFunc()` directly, but only in `result` coros. In `folly::coro` tasks, you needed to write `co_await co_ready(resultFun())`. * After the refactor, both kinds of coros write `co_await or_unwind(resultFunc())` to get the result, or propagate the error/stopped to the parent. (2) * Before the refactor, getting a lvalue ref when awaiting required `std::ref` / `std::cref`. * After the refactor, `co_await or_unwind(r)` and `co_await or_unwind(std::as_const(r))` work as expected. (3) * Starting to move out the coro-related functionality from `result/result.h` into `result/coro.h` to allow "lower dependency weight" usage of `result` where coro functionality is not needed. Reviewed By: ispeters Differential Revision: D80322048 fbshipit-source-id: 567e13835c97b7432b2bb101e72bfb48bb14f9ccAlexey Spiridonov · 0ed9cc20 · 2025-09-09
- 0.8ETVImprove arg safety of `async_now_closure` when the inner coro is safe Summary: Prior to this diff, the following natural scatter-gather pattern would be unsafe, since the `capture_mut_ref` reference would get downgraded to `after_cleanup_capture<Output&>` inside the closure. ``` Output output; co_await async_now_closure( bind::capture_mut_ref{output}, [](auto out) -> closure_task<> { // run scope tasks or `background_task`s to write to `out` }); ``` `async_now_closure` is non-negotiable for `capture_..._ref{}`, but if the closure uses a safe task type, and doesn't take any `shared_cleanup` args from the parent (e.g. `co_cleanup_capture<safe_async_scope&>`), then the safety-downgrade can be avoided. Read the new comments for the implementation details. Reviewed By: ispeters Differential Revision: D79151835 fbshipit-source-id: 18434d2446e1acc815b1c00c5317be65a25f0123Alexey Spiridonov · e8656ee3 · 2025-07-30
- 0.8ETVmore exception-safety in ThreadPoolExecutor::addThreads Summary: If creating any threads within `ThreadPoolExecutor::addThreads` or `ThreadPoolExecutor::setNumThreads` fails with an exception, restore the thread pool to the state before the call. Reviewed By: ilvokhin Differential Revision: D93247730 fbshipit-source-id: 2cf1851fc8299034d09a65f334bf455a1d4fe926Yedidya Feldblum · b1d39386 · 2026-03-24
- 0.8ETV`detail/AsyncClosureBindings.h` binds `capture`s to `async_closure`s Summary: The top-of-file docblock explains how this fits into the overall implementation of `async_closure`. Reviewed By: ispeters Differential Revision: D63299828 fbshipit-source-id: 06b2cacdcdff12cc640ae6d897f78cd6d04fa237Alexey Spiridonov · a5dfbb49 · 2025-03-18
- 0.7ETVGeneralize & improve `coro/AwaitImmediately.h` Summary: This replaces `coro/AwaitImmediately.h` on-stack. As I worked on `coro` pipelines, I realized that I wanted pipe steps themselves to be able to be must-use-immediately whenever they contain unsafe refs. They're not awaitable, so using "await immediately" terminology would be a lie. Furthermore, the "await immediately" plumbing is not at all specific to coros, and I had already reimplemented a version of it in `lang/bind/`. So, I was wanting to add a third instance of a general concept, which meant it was time to clean up the abstraction. It was good that I did, because as I reworked `folly/coro` to use `MustUseImmediately.h`, I found many ways in which the old integration was subtle and magical in ways that even I no longer understood. The new, higher-level patterns shown in the test file are a little more resilient to forgetful engineers. The revamped header also has significantly better documentation (more than 50% by line count)! Reviewed By: ispeters Differential Revision: D81090509 fbshipit-source-id: 7eb06a8d77c711f688318b18750eb2d67fd9ac0eAlexey Spiridonov · a6a541c6 · 2025-09-03
- 0.7ETVuse CTAD with std::lock_guard Reviewed By: dtolnay Differential Revision: D73655940 fbshipit-source-id: d175b3ba0a4df29ba63335fab6f5db3e99bd6261Yedidya Feldblum · e5403cae · 2025-04-25